#!/usr/bin/env bash
###############################################################################
# __          _
# \ \   _ __ | |__
#  \ \ | '_ \| '_ \
#  / / | | | | |_) |
# /_/  |_| |_|_.__/
#
# [nb] Command line note-taking, bookmarking, archiving with plain-text data
# storage, encryption, filtering and search, Git-backed versioning and syncing,
# Pandoc-backed conversion, global and local notebooks, customizable color
# themes, plugins, and more in a single portable, user-friendly script.
#
# ❯ https://github.com/xwmx/nb
# ❯ https://xwmx.github.io/nb
#
# Based on Bash Boilerplate: https://github.com/xwmx/bash-boilerplate
#
# Copyright (c) 2015-present William Melody ┯ hi@williammelody.com
#                                           ┕ https://www.williammelody.com
#
# Overview
# ========
#
# - Configuration and Setup
# - Helper Functions (alphabetical order)
# - Help
# - Subcommand Functions (alphabetical order)
# - Plugins
# - Option Parsing
# - _main() / Dispatch
#
# Target Bash Versions: >= 3.2.57
#
# AGPLv3
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <https://www.gnu.org/licenses/>.
###############################################################################

###############################################################################
# Strict Mode
#
# More Information:
#   https://github.com/xwmx/bash-boilerplate#bash-strict-mode
###############################################################################

set -o nounset
set -o errexit
set -o pipefail
set -o noglob
IFS=$'\n\t'

###############################################################################
# Environment
###############################################################################

# $_VERSION
#
# The most recent program version.
_VERSION="5.7.6"

# $_ME
#
# This program's basename.
_ME="$(basename "${0}")"

# $_MY_DIR
#
# The directory containing $_ME.
_MY_DIR="$(cd "$(dirname "${0}")"; pwd)"

# $_MY_PATH
#
# This program's full path.
_MY_PATH="${_MY_DIR}/${_ME}"

# $_CURRENT_WORKING_DIR
#
# The current working directory in which the program was invoked.
_CURRENT_WORKING_DIR="${PWD}"

# $_REPO
#
# The <user>/<repo> identifier for this project's git repository.
_REPO="xwmx/nb"

# $_REPO_MAIN_BRANCH
#
# The name of the main branch in this project's git repository.
_REPO_MAIN_BRANCH="master"

# $_REPO_RAW_URL
#
# The base URL for raw files.
_REPO_RAW_URL="https://raw.githubusercontent.com/${_REPO}/${_REPO_MAIN_BRANCH}"

###############################################################################
# Utilities
###############################################################################

# _command_exists()
#
# Usage:
#   _command_exists <name>
#
# Exit / Error Status:
#   0 (success, true) If a command <name> is defined in the current environment.
#   1 (error,  false) If not.
#
# More Information:
#   http://stackoverflow.com/a/677212
_command_exists() {
  if [[ "${1:-}" == "w3m" ]]
  then # Detect WSL 1 (https://stackoverflow.com/a/38859331), where w3m errors.
    [[ -f /proc/version ]] && grep -q Micro /proc/version && return 1
  fi

  hash "${1}" 2>/dev/null
}

# _contains()
#
# Usage:
#   _contains <query> <list-item>...
#
# Exit / Error Status:
#   0 (success, true)  If the item is included in the list.
#   1 (error,  false)  If not.
#
# Example:
#   _contains "${_query}" "${_list[@]}"
_contains() {
  local _query="${1:-}"
  shift

  if [[ -z "${_query}"  ]] ||
     [[ -z "${*:-}"     ]]
  then
    return 1
  fi

  for __element in "${@}"
  do
    [[ "${__element}" == "${_query}" ]] && return 0
  done

  return 1
}

# _sed_i()
#
# Usage:
#   _sed_id <sed-argument>...
#
# Description:
#   `sed -i` takes an extension on macOS, but that extension can cause errors
#   in GNU `sed`. Detect which `sed` is available and call it with the
#   appropriate arguments.
#
# More Information:
#   https://stackoverflow.com/q/43171648
#   https://stackoverflow.com/a/16746032
_sed_i() {
  if sed --help >/dev/null 2>&1
  then # GNU
    sed -i "${@}"
  else # BSD
    sed -i '' "${@}"
  fi
}

###############################################################################
# Debug
###############################################################################

# _debug()
#
# Usage:
#   _debug <command> <options>...
#
# Description:
#   Execute a command and print to standard error. The command is expected to
#   print a message and should typically be either `echo`, `printf`, or `cat`.
#
# Example:
#   _debug printf "Debug info. Variable: %s\\n" "$0"
__DEBUG_COUNTER=0
__DEBUG_START_TIME=
_debug() {
  # Usage: __debug_get_timestamp
  __debug_get_timestamp() {
    if hash "gdate" 2>/dev/null
    then
      gdate +%s%3N
    elif date --version >/dev/null 2>&1
    then
      date +%s%3N
    else
      return 1
    fi
  }

  if ((${_USE_DEBUG:-0}))
  then
    __DEBUG_COUNTER=$((__DEBUG_COUNTER+1))
    printf "🐛  %s " "${__DEBUG_COUNTER}"

    "${@}"

    if [[ -n "${__DEBUG_START_TIME:-}" ]]
    then
      printf "⏱   %s\\n" "$(($(__debug_get_timestamp)-__DEBUG_START_TIME))"
    elif __DEBUG_START_TIME="$(__debug_get_timestamp)"
    then
      printf "⏱   0\\n"
    fi

    printf "―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――\\n"
  fi 1>&2
}

###############################################################################
# Error Messages
###############################################################################

# _exit_1()
#
# Usage:
#   _exit_1 <command>
#
# Description:
#   Exit with status 1 after executing the specified command with output
#   redirected to standard error. The command is expected to print a message
#   and should typically be either `echo`, `printf`, or `cat`.
_exit_1() {
  {
    [[ "${1:-}" == "_help" ]] || printf "%s " "$(tput setaf 1)!$(tput sgr0)"
    "${@}"
  } 1>&2
  exit 1
}

# _warn()
#
# Usage:
#   _warn <command>
#
# Description:
#   Print the specified command with output redirected to standard error.
#   The command is expected to print a message and should typically be either
#   `echo`, `printf`, or `cat`.
_warn() {
  {
    printf "%s " "$(tput setaf 1)!$(tput sgr0)"
    "${@}"
  } 1>&2
}

###############################################################################
# Option Helpers
###############################################################################

# _option_get_value()
#
# Usage:
#   _option_get_value <option> <value>
#
# Description:
#   Given a flag (e.g., -e | --example) return the value or exit 1 if value
#   is blank or appears to be another option.
_option_get_value() {
  local __arg="${1:-}"
  local __val="${2:-}"

  if [[ -n "${__val:-}" ]] && [[ ! "${__val:-}" =~ ^- ]]
  then
    printf "%s\\n" "${__val}"
  else
    _exit_1 printf                        \
      "%s requires a valid argument.\\n"  \
      "$(_color_primary "${__arg}")"
  fi
}

# _option_value_is_present()
#
# Usage:
#   _option_value_is_present <value>
#
# Exit / Error Status:
#   0 (success, true)  The argument is present and does not match as an option
#                      flag.
#   1 (error,  false)  The argument is blank or matches as an option flag.
_option_value_is_present() {
  [[ -n "${1:-}" ]] && [[ ! "${1:-}" =~ ^- ]]
}

###############################################################################
# Configuration
###############################################################################

# Configuration File ####################################### Configuration File

# $NBRC_PATH
#
# Default: `$HOME/.nbrc`
#
# The location of the .nbrc configuration file.
export NBRC_PATH="${NBRC_PATH:-"${HOME}/.nbrc"}"

# Handle symlinked NBRC_PATH.
if [[ -L "${NBRC_PATH}" ]]
then
  NBRC_PATH="$(readlink "${NBRC_PATH}")"
fi

# Source rc file.
if [[ -e "${NBRC_PATH}" ]]
then
  source "${NBRC_PATH}"
fi

# Base Configuration ####################################### Base Configuration

# $NB_DIR
#
# Default: `$HOME/.nb`
#
# The location of the directory that contains the notebooks.
export NB_DIR="${NB_DIR:-"${HOME}/.nb"}"

# Handle symlinked NB_DIR.
if [[ -L "${NB_DIR:-}"  ]]
then
  NB_DIR="$(readlink "${NB_DIR}")"
fi

# Validate that NB_DIR exists and is writable.
if [[ -z "${NB_DIR:?}"   ]] ||
   [[ "${NB_DIR}" == "/" ]] ||
   {
     [[ -e "${NB_DIR}"   ]] &&
     [[ ! -w "${NB_DIR}" ]]
   }
then
  _exit_1 cat <<HEREDOC
NB_DIR is not valid:
  ${NB_DIR}

Remove any NB_DIR settings in .nbrc to reset to default:
  ${NBRC_PATH}

NB_DIR settings prompt:
  ${_ME} settings nb_dir
HEREDOC
fi

# $NB_AUTO_SYNC
#
# Default: '1'
#
# When set to '1', each `_git checkpoint()` call will automativally run
# `$_ME sync`. To disable this behavior, set the value to '0'.
export NB_AUTO_SYNC="${NB_AUTO_SYNC:-1}"

# `$NB_DEFAULT_EXTENSION`
#
# Default: 'md'
#
# Example Values: 'md' 'org'
export NB_DEFAULT_EXTENSION="${NB_DEFAULT_EXTENSION:-md}"

# $NB_ENCRYPTION_TOOL
#
# Default: 'openssl'
#
# Supported Values: 'gpg' 'openssl'
export NB_ENCRYPTION_TOOL="${NB_ENCRYPTION_TOOL:-openssl}"

# $NB_FOOTER
#
# Default: '1'
#
# Supported Values: '0' '1'
export NB_FOOTER="${NB_FOOTER:-1}"

# $NB_HEADER
#
# Default: '2'
#
# Supported Values: '0' '1' '2' '3'
export NB_HEADER="${NB_HEADER:-2}"

# $NB_LIMIT
#
# Default: '20'
#
# Supported Values: any positive number
export NB_LIMIT="${NB_LIMIT:-20}"

# $NB_SYNTAX_THEME
#
# Default: 'base16'
#
# Supported Values: Theme names listed with `bat --list-themes`
export NB_SYNTAX_THEME="${NB_SYNTAX_THEME:-base16}"

# $_GIT_ENABLED
#
# Default: '1'
#
# Supported Values: '0' '1'
export _GIT_ENABLED="${_GIT_ENABLED:-1}"

# $EDITOR ############################################################# $EDITOR

# Set default $EDITOR if one has not been set.
export EDITOR="${EDITOR:-}"
__set_editor() {
  local _editors=(
    code
    subl
    micro
    mate
    macdown
    nano
    pico
    vim
    vi
    emacs
  )

  if [[ -z "${EDITOR:-}" ]]
  then
    if [[ -n "${VISUAL:-}" ]]
    then
      EDITOR="${VISUAL}"
    else
      for __editor in "${_editors[@]}"
      do
        if hash "${__editor}" 2>/dev/null
        then
          EDITOR="${__editor}"
          break
        fi
      done
    fi

    if [[ -z "${EDITOR:-}" ]]
    then
      cat <<HEREDOC
Command line text editor not found. Set the \$EDITOR variable in your
environment or in the \`${_ME}\` configuration file located at:
  ${NBRC_PATH}

More information about setting \$EDITOR:
  https://askubuntu.com/q/432524

\`${_ME}\` uses text editors with command line support, such as:

- Visual Studio Code
    https://code.visualstudio.com
- Sublime Text
    https://www.sublimetext.com
- Atom
    https://atom.io
- MacDown
    https://macdown.uranusjr.com
- Vim
    https://en.wikipedia.org/wiki/Vim_(text_editor)
- Emacs
    https://en.wikipedia.org/wiki/Emacs
- TextMate
    https://macromates.com
- nano
    https://en.wikipedia.org/wiki/GNU_nano
- or many of these:
    https://en.wikipedia.org/wiki/List_of_text_editors
HEREDOC
      exit 1
    fi
  fi
}; __set_editor

# Color ################################################################# Color

# $_COLOR_ENABLED
#
# Default: '1'
#
# Supported Values: '0' '1'
export _COLOR_ENABLED="${_COLOR_ENABLED:-1}"

# $NB_COLOR_THEME
#
# Default: 'nb'
#
# The color theme.
export NB_COLOR_THEME="${NB_COLOR_THEME:-nb}"

case "${NB_COLOR_THEME}" in
  nb)
    : # Colors assigned as default values below.
    ;;
  blacklight)
    export NB_COLOR_PRIMARY="${NB_COLOR_PRIMARY:-39}"
    export NB_COLOR_SECONDARY="${NB_COLOR_SECONDARY:-56}"
    ;;
  console)
    export NB_COLOR_PRIMARY="${NB_COLOR_PRIMARY:-40}"
    export NB_COLOR_SECONDARY="${NB_COLOR_SECONDARY:-28}"
    ;;
  desert)
    export NB_COLOR_PRIMARY="${NB_COLOR_PRIMARY:-179}"
    export NB_COLOR_SECONDARY="${NB_COLOR_SECONDARY:-95}"
    ;;
  electro)
    export NB_COLOR_PRIMARY="${NB_COLOR_PRIMARY:-200}"
    export NB_COLOR_SECONDARY="${NB_COLOR_SECONDARY:-26}"
    ;;
  forest)
    export NB_COLOR_PRIMARY="${NB_COLOR_PRIMARY:-29}"
    export NB_COLOR_SECONDARY="${NB_COLOR_SECONDARY:-94}"
    ;;
  monochrome)
    export NB_COLOR_PRIMARY="${NB_COLOR_PRIMARY:-248}"
    export NB_COLOR_SECONDARY="${NB_COLOR_SECONDARY:-241}"
    ;;
  ocean)
    export NB_COLOR_PRIMARY="${NB_COLOR_PRIMARY:-75}"
    export NB_COLOR_SECONDARY="${NB_COLOR_SECONDARY:-26}"
    ;;
  raspberry)
    export NB_COLOR_PRIMARY="${NB_COLOR_PRIMARY:-162}"
    export NB_COLOR_SECONDARY="${NB_COLOR_SECONDARY:-90}"
    ;;
  unicorn)
    export NB_COLOR_PRIMARY="${NB_COLOR_PRIMARY:-183}"
    export NB_COLOR_SECONDARY="${NB_COLOR_SECONDARY:-153}"
    ;;
  utility)
    export NB_COLOR_PRIMARY="${NB_COLOR_PRIMARY:-227}"
    export NB_COLOR_SECONDARY="${NB_COLOR_SECONDARY:-8}"
    ;;
esac

export _NB_COLOR_THEMES=(
  blacklight
  console
  desert
  electro
  forest
  monochrome
  nb
  ocean
  raspberry
  unicorn
  utility
)

# User defined themes can be installed in the $NB_DIR/.plugins directory.
# Themes have an .nb-theme extension and contain a single if statment
# assigning the color environment variables to tput ANSI color numbers.
#
# Example:
#
#     # filename: ~/.nb/.plugins/example.nb-theme
#     if [[ "${NB_COLOR_THEME}" == "example" ]]
#     then
#       export NB_COLOR_PRIMARY=68
#       export NB_COLOR_SECONDARY=8
#     fi
#
#  To view a list of available color numbers, run `nb settings colors`
__load_themes() {
  if [[ -d "${NB_DIR}/.themes"    ]] &&
     [[ ! -e "${NB_DIR}/.plugins" ]]
  then # migrate legacy .themes directory. TODO: Remove.
    mv "${NB_DIR}/.themes" "${NB_DIR}/.plugins"
  fi

  if [[ -d "${NB_DIR}/.plugins" ]]
  then
    set +f
    for __file in "${NB_DIR}/.plugins"/*.nb-theme*
    do
      if [[ -e "${__file}" ]]
      then
        local _basename
        _basename="$(basename "${__file}")"
        local _name="${_basename%%.*}"

        _NB_COLOR_THEMES+=("${_name}")
        source "${__file}"
      fi
    done
    set -f
  fi
}; __load_themes

# $NB_COLOR_PRIMARY
#
# Default: Value depends on terminal capabilities.
#
# Set highlighting color. This should be set to an xterm color number, usually
# a value between 1 and 256. For a table of common colors and their numbers
# run:
#   nb settings colors
#
# Supported Values: [0..255+]
__set_color_primary() {
  local _colors=
  _colors="$(tput colors)"

  if [[ -n "${_colors}" ]] && [[ "${_colors}" -gt 8 ]]
  then
    export NB_COLOR_PRIMARY="${NB_COLOR_PRIMARY:-69}"
  else
    export NB_COLOR_PRIMARY="${NB_COLOR_PRIMARY:-4}"
  fi
}; __set_color_primary

# $NB_COLOR_SECONDARY
#
# Default: '8'
#
# Color for lines and other accents. This should be set to an xterm color
# number, usually a value between 1 and 256. For a table of common colors and
# their numbers, run:
#   nb settings colors
#
# Supported Values: [0..255+]
export NB_COLOR_SECONDARY="${NB_COLOR_SECONDARY:-8}"

# Color Variables
#
# Avoid multiple tput calls by assigning output to variables.
export _TPUT_SGR0=
if [[ -n "${TMUX:-}" ]]
then
  # Avoid `tmux` and `less` quirks: https://unix.stackexchange.com/a/446541
  _TPUT_SGR0="$(printf '\033[m')"
else
  _TPUT_SGR0="$(tput sgr0)"
fi
export _TPUT_SMUL=          ; _TPUT_SMUL="$(tput smul)"
export _TPUT_SETAF_8=       ; _TPUT_SETAF_8="$(tput setaf 8)"
export _TPUT_COLOR_PRIMARY=
       _TPUT_COLOR_PRIMARY="$(tput setaf "${NB_COLOR_PRIMARY}")"

# _color_brackets()
#
# Usage:
#   _color_brackets <string>
#
# Description:
#   Print <string> with surrounding color brackets.
_color_brackets() {
  if ! ((_COLOR_ENABLED))
  then
    printf "%s[%s]\\n" "${_TPUT_SGR0}" "${1:-}"
    return 0
  fi

  printf "%s%s[%s%s%s]%s"     \
    "${_TPUT_SGR0}"           \
    "${_TPUT_SETAF_8}"        \
    "${_TPUT_COLOR_PRIMARY}"  \
    "${1:-}"                  \
    "${_TPUT_SETAF_8}"        \
    "${_TPUT_SGR0}"
}

# _color_dim()
#
# Usage:
#   _color_dim <string>
#
# Print the given string with the dim color.
_color_dim() {
  if ! ((_COLOR_ENABLED))
  then
    printf "%s%s\\n" "${_TPUT_SGR0}" "${1:-}"
    return 0
  fi

  printf "%s%s%s%s"     \
    "${_TPUT_SGR0}"     \
    "${_TPUT_SETAF_8}"  \
    "${1:-}"            \
    "${_TPUT_SGR0}"
}

# _color_primary()
#
# Usage:
#   _color_primary <string> [--underline]
#
# Description:
#   Use `tput` to highlight the given string.
_color_primary() {
  if ! ((_COLOR_ENABLED))
  then
    printf "%s%s\\n" "${_TPUT_SGR0}" "${1:-}"
    return 0
  fi

  if [[ "${2:-}" == "--underline" ]]
  then
    printf "%s%s%s%s%s\\n"      \
      "${_TPUT_SGR0}"           \
      "${_TPUT_SMUL}"           \
      "${_TPUT_COLOR_PRIMARY}"  \
      "${1:-}"                  \
      "${_TPUT_SGR0}"
  else
    printf "%s%s%s%s\\n"        \
      "${_TPUT_SGR0}"           \
      "${_TPUT_COLOR_PRIMARY}"  \
      "${1:-}"                  \
      "${_TPUT_SGR0}"
  fi
}

# _color_secondary()
#
# Usage:
#   _color_secondary <string>
#
# Description:
#   Highlight the given string using the accent color.
export _TPUT_COLOR_SECONDARY=
_TPUT_COLOR_SECONDARY="$(tput setaf "${NB_COLOR_SECONDARY}")"
_color_secondary() {
  if ! ((_COLOR_ENABLED))
  then
    printf "%s%s\\n" "${_TPUT_SGR0}" "${1:-}"
    return 0
  fi

  printf "%s%s%s\\n" "${_TPUT_COLOR_SECONDARY}" "${1:-}" "${_TPUT_SGR0}"
}

# Current Notebook ########################################### Current Notebook

# $NB_NOTEBOOK_PATH
#
# Default: `$NB_DIR/home`
#
# The path to the current notebook.
export NB_NOTEBOOK_PATH="${NB_NOTEBOOK_PATH:-"${NB_DIR}/home"}"

# $_GLOBAL_NOTEBOOK_PATH
#
# The path of the current global notebook.
export _GLOBAL_NOTEBOOK_PATH=

# $_LOCAL_NOTEBOOK_PATH
#
# The path of the local notebook, if one is found. The current working
# directory and all parents are searched.
export _LOCAL_NOTEBOOK_PATH=

# __set_notebook_paths()
#
# Usage:
#   __set_notebook_paths
#
# Description:
#   Set the the notebook path variables to reflect the current environment.
__set_notebook_paths() {
  __set_local_notebook_path() {
    if [[ "${PWD}" == "/" ]]
    then
      cd "${_CURRENT_WORKING_DIR}"
      return 0
    elif [[ -d "${PWD}/.git"    ]] &&
         [[ -f "${PWD}/.index"  ]]
    then
      _LOCAL_NOTEBOOK_PATH="${PWD}"
      cd "${_CURRENT_WORKING_DIR}"
      return 0
    else
      cd ..
      __set_local_notebook_path
    fi
  }; __set_local_notebook_path

  __set_global_notebook_path() {
    if [[ -e "${NB_DIR}/.current" ]]
    then
      local _global_notebook_name=
      _global_notebook_name="$(<"${NB_DIR}/.current")"

      if [[ -d "${NB_DIR}/${_global_notebook_name}" ]]
      then
        _GLOBAL_NOTEBOOK_PATH="${NB_DIR}/${_global_notebook_name}"
      fi
    fi
  }; __set_global_notebook_path

  if [[ -n "${_LOCAL_NOTEBOOK_PATH:-}" ]]
  then
    NB_NOTEBOOK_PATH="${_LOCAL_NOTEBOOK_PATH}"
  else
    NB_NOTEBOOK_PATH="${_GLOBAL_NOTEBOOK_PATH}"
  fi
}; __set_notebook_paths

# Cache & Temp ################################################### Cache & Temp

# $_NB_CACHE_PATH
#
# The full path to the cache directory.
export _NB_CACHE_PATH="${NB_DIR}/.cache"
if [[ -d "${NB_DIR}" ]] && [[ ! -e "${_NB_CACHE_PATH}" ]]
then
  mkdir -p "${_NB_CACHE_PATH}"
fi

# $TMPDIR
#
# Reset $TMPDIR if it's set to the current working directory by npm.
if [[ "${TMPDIR:-}" == "${PWD}" ]]
then
  export TMPDIR="/tmp"
fi

# $_NB_TEMP_DIRECTORY
#
# The full path to the temp directory.
export _NB_TEMP_DIRECTORY
_NB_TEMP_DIRECTORY="$(mktemp -d)"

# _temp()
#
# Usage:
#   _temp cache [clear]
#   _temp directory
#   _temp file <basename>
#   _temp cleanup
#
# Subcommands:
#   cache       Print the full path to the cache directory, or optionally clear
#               the cache.
#   directory   Print the full path the the temp directory.
#   file        Print the full path for <basename> within the temp directory.
#   cleanup     Force delete the temp directory.
# Description:
#   Manage temp and cache files and directories.
_temp() {
  # _temp_validate_directory <path>
  _temp_validate_directory() {
    [[ -n "${1:?}"          ]] &&
    [[ -d "${1}"            ]] &&
    [[ "${1}" != "/"        ]] &&
    [[ "${1}" != "${HOME}"  ]] &&
    [[ "${1}" != "${PWD}"   ]]
  }

  local _subcommand="${1:-}"

  case "${_subcommand}" in
    cache)
      if [[ "${2:-}" == "clear" ]]
      then
        if _temp_validate_directory "${_NB_CACHE_PATH:?}"
        then
          rm -r "${_NB_CACHE_PATH:?}"
          mkdir -p "${_NB_CACHE_PATH}"
        fi
      else
        printf "%s\\n" "${_NB_CACHE_PATH}"
      fi
      ;;
    directory)
      printf "%s\\n" "${_NB_TEMP_DIRECTORY:-}"
      ;;
    file)
      [[ -z "${2:-}" ]] && return 1
      printf "%s/%s" "${_NB_TEMP_DIRECTORY}" "${2:-}"
      ;;
    cleanup)
      if _temp_validate_directory "${_NB_TEMP_DIRECTORY:?}"
      then
        rm -rf "${_NB_TEMP_DIRECTORY:?}"
      fi
      ;;
  esac
}

trap "_temp cleanup" EXIT

###############################################################################
# Helpers
###############################################################################

# $_NEWLINE
#
# Assign newline with ANSI-C quoting for string building.
_NEWLINE=$'\n'

# _alias_subcommand()
#
# Usage:
#   _alias_subcommand <subcommand> <alias>
#
# Description:
#   Create an <alias> of <subcommand>. NOTE: aliases also have to be added to
#   the $_SUBCOMMANDS variable.
_alias_subcommand() {
  local _subcommand="${1:-}"
  local _alias="${2:-}"

  if [[ -z "${_subcommand}" ]] || [[ -z "${_alias}" ]]
  then
    return 1
  fi

  eval "describe \"${_alias}\" \"\$____describe_${_subcommand}\""
  eval "_${_alias}() { _${_subcommand} \"\${@}\"; }"
}

# _browser()
#
# Usage:
#   _browser [<url>] [--dump]
#
# Options:
#   --dump    Print the page to standard output.
#
# Description:
#   Interact with the preferred or available terminal web browser. When called
#   with no input or arguments, test if a terminal web browser is available.
#   With <url> or standard input, view in the preferred or available browser.
#
# Exit / Error Status:
#   0 (success, true)  If a terminal browser is available.
#   1 (error,  false)  if not.
_browser() {
  local _browser_command=()
  local _dump=0
  local _url=

  for __arg in "${@:-}"
  do
    case "${__arg}" in
      --dump)
        _dump=1
        ;;
      *)
        [[ -z "${_url:-}" ]] && _url="${__arg}"
        ;;
    esac
  done

  if [[ -n "${BROWSER:-}" ]]
  then
    IFS=' ' read -ra _browser_command <<< "${BROWSER}"
  else
    if _command_exists "w3m"
    then
      _browser_command+=("w3m")
    elif _command_exists "lynx"
    then
      _browser_command+=("lynx")
    fi
  fi

  [[ -z "${_browser_command[*]:-}" ]] && return 1

  if _interactive_input
  then
    if [[ -z "${_url:-}" ]]
    then
      return 0
    else
      "${_browser_command[@]:-}" "${_url:-}"
      return 0
    fi
  else
    if [[ "${_browser_command[*]:-}" =~ w3m ]]
    then
      if ((_dump))
      then
        "${_browser_command[@]:-}" -T text/html -dump
      else
        "${_browser_command[@]:-}" -T text/html
      fi

      return 0
    elif [[ "${_browser_command[*]:-}" =~ lynx ]]
    then
      _arguments+=("-stdin")
      ((_dump)) && _arguments+=("--dump")

      "${_browser_command[@]:-}" "${_arguments[@]:-}"
      return 0
    else
      "${_browser_command[@]}"
      return 0
    fi
  fi
}

# _decrypt_file()
#
# Usage:
#   _decrypt_file <path> <password>
_decrypt_file() {
  local _encrypted_path="${1:-}"
  local _password="${2:-}"

  if [[ -z "${_password}" ]]
  then
    _exit_1 printf "Password required.\\n"
  fi

  if [[ -z "${_encrypted_path}" ]]
  then
    return 1
  fi

  local _basename
  _basename="$(basename "${_encrypted_path}")"

  local _unencrypted_basename
  _unencrypted_basename="$(printf "%s" "${_basename}" | sed 's/.enc$//')"

  local _unencrypted_path
  _unencrypted_path="$(_temp file "${_unencrypted_basename}")"

  local _file_command_response
  _file_command_response="$(file "${_encrypted_path}")"

  if [[ "${_file_command_response}" =~ GPG ]]
  then
    if _command_exists "gpg"
    then
      printf "%s\\n" "${_password}"       \
        | gpg                             \
          --quiet                         \
          --batch                         \
          --passphrase-fd 0               \
          --output "${_unencrypted_path}" \
          --decrypt "${_encrypted_path}"
    else
      _exit_1 printf "\
This note was encrypted with GPG, but the GPG command was not found.\\n"
    fi
  elif [[ "${_file_command_response}" =~ openssl ]]
  then
    openssl enc                           \
      -d                                  \
      -aes-256-cbc                        \
      -in "${_encrypted_path}"            \
      -out "${_unencrypted_path}"         \
      -pass file:<(printf "%s\\n" "${_password}") 2> /dev/null ||
        rm "${_unencrypted_path:?}"
  else
    _exit_1 printf "Unable to decrypt file.\\n"
  fi

  if [[ ! -e "${_unencrypted_path}" ]]
  then
    _exit_1 printf "Decryption error.\\n" 1>&2
  fi

  printf "%s\\n" "${_unencrypted_path}"
}

# _download_from()
#
# Usage:
#   _download_from <url> [<outfile>]
#
# Description:
#   Download the file at <url> and print to standard output or <outfile>, if
#   present.
_download_from() {
  local _downloaded=0
  local _timeout=15
  local _url="${1:-}"

  if [[ -z "${_url}" ]] ||
     [[ ! "${_url}" =~ ^https\:|^http\:|^file\:|^ftp\:|^sftp\: ]]
  then
    return 1
  fi

  local _target_path="${2:-}"

  if [[ -n "${_target_path}" ]]
  then
    if _command_exists "curl"
    then
      curl                              \
        --silent                        \
        --location                      \
        --connect-timeout "${_timeout}" \
        "${_url}"                       \
        --output "${_target_path}"      \
        && _downloaded=1
    elif _command_exists "wget"
    then
      wget                              \
        --quiet                         \
        --connect-timeout="${_timeout}" \
        --dns-timeout="${_timeout}"     \
        -O "${_target_path}"            \
        "${_url}"                       \
        2>/dev/null                     \
        && _downloaded=1
    fi
  else
    if _command_exists "curl"
    then
      curl                              \
        --silent                        \
        --location                      \
        --connect-timeout "${_timeout}" \
        "${_url}"                       \
        && _downloaded=1
    elif _command_exists "wget"
    then
      wget                              \
        --quiet                         \
        --connect-timeout="${_timeout}" \
        --dns-timeout="${_timeout}"     \
        -O -                            \
        "${_url}"                       \
        2>/dev/null                     \
        && _downloaded=1
    fi
  fi

  if ! ((_downloaded))
  then
    return 1
  fi
}

# _edit_file()
#
# Usage:
#   _edit_file <path> [--no-wait]
#
# Description:
#   Open the file in $EDITOR. Use the `-f` option in vim and `--wait` option in
#   other GUI editors to wait until the file is closed in the editor before
#   continuing, unless `--no-wait`.
_edit_file() {
  local _file_path="${1:-}"
  local _wait=1

  for __arg in "${@:-}"
  do
    case "${__arg}" in
      --no-wait)
        _wait=0
        ;;
      *)
        if [[ -z "${_file_path}" ]]
        then
          _file_path="${__arg}"
        fi
        ;;
    esac
  done

  if [[ -z "${_file_path}" ]]
  then
    return 1
  fi

  local _current_notebook_path
  _current_notebook_path="$(_notebooks current --path)"

  cd "${_current_notebook_path}" || return 1

  if [[ -e "${_file_path}" ]]
  then
    if _file_is_encrypted "${_file_path}"
    then
      _exit_1 printf "\
File must be decrypted before editing.\\n"
    fi

    if _file_is_archive "${_file_path}" &&
       [[ ! "${_file_path}" =~ docx$ ]]
    then
      _exit_1 printf "\
Can't edit archives. Export archive and expand to edit.\\n"
    fi

    if ! _file_is_text "${_file_path}" &&
       [[ ! -d "${_file_path}" ]]
    then
      if _command_exists "xdg-open"
      then
        xdg-open "${_file_path}" && return 0
      elif [[ "${OSTYPE}" =~ ^darwin ]]
      then
        open "${_file_path}" && return 0
      fi
    fi
  fi

  local _editor_command="${EDITOR}"

  if ((_wait)) &&
     [[ "${EDITOR}" =~ gvim|mvim ]]
  then
    _editor_command="${_editor_command} -f"
  elif ((_wait)) &&
       [[ "${EDITOR}" =~ code|mate|subl ]]
  then
    _editor_command="${_editor_command} --wait"
  fi

  if [[ -d "${_file_path}" ]] &&
     [[ "${EDITOR}" =~ vim ]]
  then
    cd "${_file_path}" && "${_editor_command:-}"
  else
    if [[ "${EDITOR}" =~ typora   ]] &&
       [[ ! -f "${_file_path:-}"  ]]
    then
      touch "${_file_path:-}"
    fi

    eval "${_editor_command} \"${_file_path}\""
  fi
}

# _encrypt_file()
#
# Usage:
#   _encrypt_file <unencrypted-path> <encrypted-path> <password>
_encrypt_file() {
  local _unencrypted_path="${1:-}"
  local _encrypted_path="${2:-}"
  local _password="${3:-}"

  if [[ -z "${_password}" ]]
  then
    _exit_1 printf "Password required.\\n"
  fi

  if [[ -z "${_unencrypted_path}" ]] ||
     [[ -z "${_encrypted_path}"   ]]
  then
    _exit_1 printf "Encrypted and unencrypted paths required.\\n"
  fi

  if [[ "${NB_ENCRYPTION_TOOL}" == "gpg" ]]
  then
    printf "%s\\n" "${_password}"     \
      | gpg                           \
        --quiet                       \
        --batch                       \
        --passphrase-fd 0             \
        --symmetric                   \
        --cipher-algo AES256          \
        --output "${_encrypted_path}" \
        "${_unencrypted_path}"
  elif [[ "${NB_ENCRYPTION_TOOL}" == "openssl" ]]
  then
    openssl enc                       \
      -aes-256-cbc                    \
      -in "${_unencrypted_path}"      \
      -out "${_encrypted_path}"       \
      -pass file:<(printf "%s\\n" "${_password}") 2> /dev/null
  else
    _exit_1 \
      printf "\$NB_ENCRYPTION_TOOL must be set to 'gpg' or 'openssl'.\\n"
  fi

  if [[ ! -e "${_encrypted_path}" ]]
  then
    _exit_1 printf "Encryption error.\\n"
  fi
}

# _file_is_archive()
#
# Usage:
#   _file_is_archive <path>
#
# Exit / Error Status:
#   0 (success, true)  If file is an archive.
#   1 (error,  false)  if not.
export _ARCHIVE_FILE_EXTENSIONS=(
  7z
  apk
  bz2
  dmg
  gz
  lz
  rar
  s7z
  sit
  sitx
  sparsebundle
  tar
  tgz
  tbz2
  tlz
  txz
  xz
  zip
  zipx
  Z
)
_file_is_archive() {
  local _file_path="${1:-}"
  local _file_type="${_file_path##*.}"

  LC_ALL=C _contains "${_file_type}" "${_ARCHIVE_FILE_EXTENSIONS[@]}"
}

# _file_is_audio()
#
# Usage:
#   _file_is_audio (<path> | <filename>)
#
# Exit / Error Status:
#   0 (success, true)  If file is an audio file.
#   1 (error,  false)  if not.
export _AUDIO_FILE_EXTENSIONS=(
  aac
  aiff
  flac
  m4a
  mp3
  ogg
  wav
)
_file_is_audio() {
  local _file_path="${1:-}"
  local _file_type="${_file_path##*.}"

  LC_ALL=C _contains "${_file_type}" "${_AUDIO_FILE_EXTENSIONS[@]}"
}

# _file_is_bookmark()
#
# Usage:
#   _file_is_bookmark (<path> | <filename>)
#
# Exit / Error Status:
#   0 (success, true) If file is a bookmark.
#   1 (error,  false) if not.
export _BOOKMARK_FILE_EXTENSIONS=(
  bookmark.md
  bookmark.md.enc
)
_file_is_bookmark() {
  # Use explicit matching for legacy bookmark name support.
  [[ "${1:-}" =~ [-|.]bookmark.md|[-|.]bookmark.md.enc ]]
}

# _file_is_document()
#
# Usage:
#   _file_is_document (<path> | <filename>)
#
# Exit / Error Status:
#   0 (success, true)  If file is a Word, Open Office, PDF, or other document.
#   1 (error,  false)  if not.
export _DOCUMENT_FILE_EXTENSIONS=(
  doc
  docx
  odt
  pdf
  rtf
  xls
  xlsx
)
_file_is_document() {
  local _file_path="${1:-}"
  local _file_type="${_file_path##*.}"

  LC_ALL=C _contains "${_file_type}" "${_DOCUMENT_FILE_EXTENSIONS[@]}"
}

# _file_is_encrypted()
#
# Usage:
#   _file_is_encrypted <path>
#
# Exit / Error Status:
#   0 (success, true)  If file is encrypted.
#   1 (error,  false)  if not.
export _ENCRYPTED_FILE_EXTENSIONS=(
  enc
)
_file_is_encrypted() {
  local _file_path="${1:-}"
  local _file_type="${_file_path##*.}"

  # `file` with 'soft' test is slow, so avoid calling it.
  LC_ALL=C _contains "${_file_type}" "${_ENCRYPTED_FILE_EXTENSIONS[@]}" ||
    {
      ! _file_is_text     "${_file_path:-}" &&
      ! _file_is_image    "${_file_path:-}" &&
      ! _file_is_document "${_file_path:-}" &&
      [[ "$(file "${_file_path:-}"  \
              --exclude=apptype     \
              --exclude=encoding    \
              --exclude=tokens      \
              --exclude=cdf         \
              --exclude=compress    \
              --exclude=elf         \
              --exclude=tar)" =~ encrypted|openssl ]]
    }
}

# _file_is_image()
#
# Usage:
#   _file_is_image (<path> | <filename>)
#
# Exit / Error Status:
#   0 (success, true)  If file is an image.
#   1 (error,  false)  if not.
export _IMAGE_FILE_EXTENSIONS=(
  afphoto
  ai
  bmp
  gif
  ind
  indd
  jpg
  jpeg
  png
  psd
  raw
  svg
  svgz
  tif
  tiff
  webp
)
_file_is_image() {
  local _file_path="${1:-}"
  local _file_type="${_file_path##*.}"

  LC_ALL=C _contains "${_file_type}" "${_IMAGE_FILE_EXTENSIONS[@]}"
}

# _file_is_text()
#
# Usage:
#   _file_is_text <path>
#
# Exit / Error Status:
#   0 (success, true)  If file is text.
#   1 (error,  false)  if not.
#
# Resources:
#   Markdown: https://superuser.com/a/285878
export _TEXT_FILE_EXTENSIONS=(
  bib
  css
  html
  js
  jsx
  latex
  markdown
  md
  mdown
  mdwn
  mdtxt
  mdtext
  mkd
  mkdn
  org
  py
  rb
  Rmd
  rst
  scss
  sh
  tex
  text
  textile
  txt
  xml
)
_file_is_text() {
  local _file_path="${1:-}"
  local _file_type="${_file_path##*.}"

  # Avoid calling `file` for better performance.
  LC_ALL=C _contains "${_file_type}" "${_TEXT_FILE_EXTENSIONS[@]}"      ||
    {
      ! _file_is_image    "${_file_path:-}"                             &&
      ! _file_is_document "${_file_path:-}"                             &&
      ! _file_is_audio    "${_file_path:-}"                             &&
      ! _file_is_video    "${_file_path:-}"                             &&
      ! _file_is_archive  "${_file_path:-}"                             &&
      [[ ! "${_file_type}" =~ \.epub$   ]]                              &&
      [[ ! -d "${_file_path:-}"         ]]                              &&
      ! _contains "${_file_type:-}" "${_ENCRYPTED_FILE_EXTENSIONS[@]}"  &&
      [[ "$(file "${_file_path:-}"  \
              --exclude=apptype     \
              --exclude=encoding    \
              --exclude=tokens      \
              --exclude=cdf         \
              --exclude=compress    \
              --exclude=elf         \
              --exclude=tar         \
              --exclude=soft        \
              -b --mime-type)" =~ ^text ]]
    }
}

# _file_is_video()
#
# Usage:
#   _file_is_video (<path> | <filename>)
#
# Exit / Error Status:
#   0 (success, true)  If file is a video file.
#   1 (error,  false)  if not.
export _VIDEO_FILE_EXTENSIONS=(
  avi
  flv
  m4p
  m4v
  mp2
  mp4
  mov
  mpeg
  mpg
  qt
  webm
  wmv
)
_file_is_video() {
  local _file_path="${1:-}"
  local _file_type="${_file_path##*.}"

  LC_ALL=C _contains "${_file_type}" "${_VIDEO_FILE_EXTENSIONS[@]}"
}

# _get_first_line()
#
# Usage:
#   _get_first_line <path>
#
# Description:
#   Print the first line of the file at <path>, skipping any front matter.
_get_first_line() {
  local _first_line=
  local _in_code_block=0
  local _in_front_matter=0
  local _path="${1:-}"

  if [[ -z "${_path}" ]]
  then
    return 1
  elif [[ -e "${_path}" ]]
  then
    if _file_is_text "${_path}"
    then

      # `awk` alternative
      # -----------------
      #       awk -f - "${_path}" <<'HEREDOC'
      # (state == 0) && /^---$/ { state=1; next }
      # (state == 1) && /^---$/ { state=0; next }
      # (state == 0) && /^```/  { state=1; next }
      # (state == 1) && /^```/  { state=0; next }
      #
      # (state == 0) && /^./    { print; exit   }
      # HEREDOC

      # Avoid single line being ignored.
      # https://stackoverflow.com/a/10929511
      while IFS= read -r __line || [[ -n "${__line:-}" ]]
      do
        if [[ "${__line}" =~ ^---$ ]]
        then
          if ((_in_front_matter))
          then
            _in_front_matter=0
          else
            _in_front_matter=1
          fi
        elif [[ "${__line}" =~ ^\`\`\` ]]
        then # start or end of code block
          if ((_in_code_block))
          then
            _in_code_block=0
          else
            _in_code_block=1
          fi
        elif ! ((_in_front_matter)) && ! ((_in_code_block))
        then
          if [[ -z "${_first_line}" ]] && [[ -n "${__line}" ]]
          then
            if [[ "${#__line}" -gt 500 ]]
            then
              _first_line="${__line:0:500}..."
            else
              _first_line="${__line}"
            fi
            break
          fi
        fi
      done < "${_path}"

      printf "%s\\n" "${_first_line}"
    fi
  fi
}

# _get_hash()
#
# Usage:
#   _get_hash <path>
#
# Description:
#   Generate a hash for the file or directory at <path>.
_get_hash() {
  # Usage: _get_hash_with_command <path> <command>
  _get_hash_with_command() {
    local _command=
    IFS=' ' read -ra _command <<< "${2:-}"

    local _path="${1:-}"

    [[ -n "${_command[*]:-}" ]] && [[ -n "${_path}" ]] || return 1

    if [[ -d "${_path}" ]]
    then
      tar -P -cf - "${_path}"     \
        | "${_command[@]}"        \
        | awk '{ print $1 }'
    else
      "${_command[@]}" "${_path}" \
        | awk '{print $1}'
    fi
  }

  local _path="${1:-}"
  [[ -n "${_path:-}" ]] || return 1

  if _command_exists "shasum"
  then
    _get_hash_with_command "${_path}" "shasum -a 256"
  elif _command_exists "md5sum"
  then
    _get_hash_with_command "${_path}" "md5sum"
  elif _command_exists "md5"
  then
    _get_hash_with_command "${_path}" "md5 -q"
  else
    _exit_1 printf "No hashing tool found.\\n"
  fi
}

# _get_http_status()
#
# Usage:
#   _get_http_status <url>
#
# Description:
#   Print the http status response code for <url>.
_get_http_status() {
  local _url="${1:-}"
  [[ -n "${_url:-}" ]] || return 1

  if _command_exists "curl"
  then
    curl -I -s -o /dev/null -w "%{http_code}\\n" "${_url}" || return 0
  elif _command_exists "wget"
  then
    wget -S "${_url}" 2>&1 | grep "HTTP/" | awk '{print $2}' || return 0
  fi
}

# _get_title()
#
# Usage:
#   _get_title <path>
#
# Description:
#   Print the title, if present, of the note at the path. Supports both
#   Markdown h1 styles and YAML front matter.
#
#   - Markdown
#     - Supported:
#       - # This is a Title
#       - This is a Title
#         ===============
#       - ---
#         title: This is a Title
#         ---
#     - https://daringfireball.net/projects/markdown/syntax#header
#     - https://jekyllrb.com/docs/front-matter/
#   - Org Mode
#     - Supported:
#       - #+TITLE: This is a Title
#     - https://orgmode.org/guide/Export-Settings.html
#   - Latex
#     - Supported:
#       - \title{This is a Title}
_get_title() {
  local _counter=0
  local _in_code_block=0
  local _in_front_matter=0
  local _maybe_title=
  local _path="${1:-}"
  local _title=

  if [[ -z "${_path}" ]]
  then
    return 1
  elif [[ -e "${_path}" ]]
  then
    if [[ "${_path}" =~ \.md$|\.markdown$ ]]
    then
      while IFS= read -r __line || [[ -n "${__line}" ]]
      do
        if [[ "${__line}" =~ ^([[:space:]]*#[[:space:]]+) ]] &&
           ! ((_in_code_block))
        then # line starts with an atx-style H1
          _title="${__line#${BASH_REMATCH[1]}}"

          if [[ "${_title}" =~ ([[:space:]]+#[[:space:]]*)$ ]]
          then
            _title="${_title%${BASH_REMATCH[1]}}"
          fi

          # `sed` alternative
          # -----------------
          # _title="$(
          #   printf "%s\\n" "${__line}"    \
          #     | sed                       \
          #       -e 's/^[[:space:]]*# //'  \
          #       -e 's/ #$//'
          #   )"

          break
        elif [[ "${__line}" =~ ^---$ ]]
        then # start or end of front matter block
          if ((_in_front_matter))
          then
            _in_front_matter=0
          else
            _in_front_matter=1
          fi
        elif [[ "${__line}" =~ ^\`\`\` ]]
        then # start or end of code block
          if ((_in_code_block))
          then
            _in_code_block=0
          else
            _in_code_block=1
          fi
        elif ((_in_front_matter)) && [[ "${__line}" =~ ^title\: ]]
        then # front matter title
          _title="$(printf "%s\\n" "${__line}" | sed 's/^title: //')"
          break
        elif ! ((_in_front_matter)) && ! ((_in_code_block))
        then
          if [[ -z "${_maybe_title}" ]] && [[ -n "${__line}" ]]
          then  # potential setext-style H1
            _maybe_title="${__line}"
          elif [[ -n "${_maybe_title}"             ]] &&
               [[ "${__line}" =~ ^[[:space:]]*\=+$ ]]
          then # underline for setext-style H1 on previous line
            _title="$(
              printf "%s\\n" "${_maybe_title}" | sed 's/^[[:space:]]*//'
            )"
            break
          elif [[ -n "${_maybe_title}"       ]] &&
               [[ ! "${_maybe_title}" =~ ^\[ ]]
          then # in normal content, title not found
            break
          else
            continue
          fi
        fi
      done < "${_path}"
    elif [[ "${_path}" =~ \.latex$|\.tex$ ]]
    then
      while IFS= read -r __line || [[ -n "${__line}" ]]
      do
        _counter="$((_counter+1))"

        if [[ "${__line}" =~  ^\\title\{ ]]
        then
          _title="$(
            printf "%s\\n" "${__line}" | sed 's/\\title{\(.*\)}/\1/'
          )"
          break
        elif [[ -n "${__line:-}"      ]] &&
             [[ ! "${__line}" =~ ^\\  ]] &&
             [[ "${_counter}" -gt 20  ]]
        then
          break
        else
          continue
        fi
      done < "${_path}"
    elif [[ "${_path}" =~ \.org$ ]]
    then
      while IFS= read -r __line || [[ -n "${__line}" ]]
      do
        _counter="$((_counter+1))"

        # NOTE: Titles can be defined with multiple #+TITLE: lines.
        if [[ "${__line}" =~ ^\#\+TITLE\: ]]
        then
          if [[ -n "${_title:-}" ]]
          then
            _title="${_title} "
          fi

          _title+="$(printf "%s\\n" "${__line}" | sed 's/^#+TITLE: //')"
        elif [[ -n "${__line:-}"      ]] &&
             [[ ! "${__line}" =~ ^\#  ]] &&
             [[ "${_counter}" -gt 5   ]]
        then
          break
        else
          continue
        fi
      done < "${_path}"
    fi

    printf "%s\\n" "${_title}"
  fi
}

# _get_unique_basename()
#
# Usage:
#   _get_unique_basename [<file-name> | <file-extension>] [<directory>]
#
# Description:
#   Get a unique basename that doesn't conflict with any existing file.
_get_unique_basename() {
  local _file_basename="${1:-}"
  local _file_name="${_file_basename%%.*}"

  local _directory="${2:-}"

  if [[ -z "${_directory:-}" ]]
  then
    _directory="$(_notebooks current --path)"
  fi

  if [[ -z "${_file_name}" ]]
  then
    _file_name="$(date "+%Y%m%d%H%M%S")"
  fi

  local _file_type="${_file_basename#*.}"
  if [[ -z "${_file_type}" ]]
  then
    _file_type="md"
  fi

  if [[ -n "${_file_type:-}"                ]] &&
     [[ "${_file_basename}" =~ \.           ]] &&
     [[ "${_file_name}" != "${_file_type}"  ]]
  then
    _file_type="${_file_type}"
  else
    _file_type="md"
  fi

  basename "$(
    _get_unique_path "${_directory}/${_file_name}.${_file_type}"
  )"
}

# _get_unique_path()
#
# Usage:
#   _get_unique_path <path>
#
# Description:
#   Get a unique full path that doesn't conflict with any existing file.
_get_unique_path() {
  local _path="${1:-}"

  local _dir_path=
  _dir_path="$(dirname "${_path}")"

  local _basename=
  _basename="$(basename "${_path}")"

  local _file_name="${_basename%%.*}"
  local _file_extension="${_basename#*.}"

  if [[ -n "${_file_extension:-}"               ]] &&
     [[ "${_basename}" =~ \.                    ]] &&
     [[ "${_file_name}" != "${_file_extension}" ]]
  then
    _file_extension=".${_file_extension}"
  else
    _file_extension=
  fi

  local _unique_file_name="${_file_name}"
  local _uniqueness_counter=0

  while [[ -e "${_dir_path}/${_unique_file_name}${_file_extension}"     ]] ||
        [[ -e "${_dir_path}/${_unique_file_name}${_file_extension}.enc" ]]
  do
    _uniqueness_counter="$((_uniqueness_counter+1))"

    printf -v _unique_file_name -- "%s-%01d"  \
      "${_file_name}"                         \
      "${_uniqueness_counter}"
  done

  printf "%s\\n" "${_dir_path}/${_unique_file_name}${_file_extension}"
}

# _get_visible_length()
#
# Usage:
#   _get_visible_length <string> [<extra-length>]
#
# Describe:
#   Calculate the apparent length of <string>, with byte and character
#   calculations for handling unicode characters.
#
# More Information:
#   https://stackoverflow.com/a/31009961
_get_visible_length() {
  local _string="${1:-}"
  local _extra_length="${2:-0}"

  _LANG="${LANG:-}" _LC_ALL="${LC_ALL:-}"

  local _length_chars=
  _length_chars="${#_string}"

  LANG=C LC_ALL=C

  local _length_bytes=
  _length_bytes="${#_string}"

  LANG="${_LANG:-}" LC_ALL="${_LC_ALL:-}"

  local _difference=
  _difference=$(((_length_bytes-_length_chars)/3))

  local _calculated_length=
  _calculated_length=$((_length_chars+_difference))

  if ((_extra_length))
  then
    _calculated_length=$((_calculated_length+_extra_length))
  fi

  printf "%s\\n" "${_calculated_length:-0}"
}

# _highlight_syntax_if_available()
#
# Usage:
#   _highlight_syntax_if_available [<path> | <extension>]
#
# Description:
#   If `bat` or Pygments is available, use it to highlight syntax. When neither
#   is available, pipe through `cat`.
#
# References:
#   https://github.com/sharkdp/bat
#   https://pygments.org/
_highlight_syntax_if_available() {
  local _extension=
  local _path=

  if [[ "${1:-}" =~ / ]]
  then
    _path="${1}"
  elif [[ -n "${1:-}" ]]
  then
    _extension="${1}"
  else
    _extension="md"
  fi

  if ! ((_COLOR_ENABLED))
  then
    if [[ -n "${_path:-}" ]]
    then
      cat "${_path}"
    else
      cat
    fi

    return 0
  fi

  if hash "bat" 2>/dev/null
  then
    local _arguments=()

    if [[ -n "${_path:-}" ]]
    then
      _arguments+=("${_path}")
    else
      _arguments+=("--language" "${_extension}")
    fi

    bat "${_arguments[@]}"  \
      --paging never        \
      --color always        \
      --plain               \
      --theme "${NB_SYNTAX_THEME:-base16}"
  elif hash "highlight" 2>/dev/null
  then
    local _arguments=("-O" "ansi")

    if [[ -n "${_path:-}" ]]
    then
      _arguments+=("${_path}")
    else
      _arguments+=("--syntax" "${_extension}")
    fi

    highlight "${_arguments[@]}"
  elif ((_COLOR_ENABLED)) && hash "pygmentize" 2>/dev/null
  then # pygments is installed.
    local _arguments=("-O" "style=default" "-f" "terminal")

    if [[ -n "${_path:-}" ]]
    then
      _arguments+=("${_path}")
    else
      _arguments+=("-l" "${_extension}")
    fi

    pygmentize "${_arguments[@]}" 2>/dev/null
  else
    if [[ -n "${_path:-}" ]]
    then
      cat "${_path}"
    else
      cat
    fi
  fi
}

# _interactive_input()
#
# Usage:
#   _interactive_input
#
# Exit / Error Status:
#   0 (success, true)  If the current input is interactive (eg, a shell).
#   1 (error,  false)  If the current input is stdin / piped input.
_interactive_input() {
  [[ -t 0 ]]
}

# _join()
#
# Usage:
#   _join <delimiter> <list-item>...
#
# Description:
#   Print a string containing all <list-item> arguments separated by
#   <delimeter>.
#
# Example:
#   _join "${_delimeter}" "${_list[@]}"
#
# More Information:
#   https://stackoverflow.com/a/17841619
_join() {
  local _delimiter="${1:-}"
  shift
  printf "%s" "${1:-}"
  shift
  printf "%s" "${@/#/${_delimiter}}" | tr -d '[:space:]'
}

# _less_prompt()
#
# Usage:
#   _less_prompt
#
# Description:
#   The prompt to display in `less`.
_less_prompt() {
  local _prompt="\
> scroll for more, h for help, or q to quit"

  if [[ "$(tput cols)" -gt 81 ]]
  then
    _prompt="\
> scroll for more, f / b to jump ↓ / ↑, h for help, or q to quit"
  fi

  printf "%s\\n" "${_prompt}"
}

# _wrap()
#
# Usage:
#   _wrap [on | off]
#
# Description:
#   Turn on line wrapping / automatic margins.
_wrap() {
  if [[ "${1:-}" == "off"  ]]
  then
    # NOTE: `tput` vs escape sequence:
    # https://unix.stackexchange.com/a/558771
    # https://unix.stackexchange.com/a/515938
    # https://superuser.com/a/189068

    # tput rmam
    printf '\033[?7l'
  elif [[ "${1:-}" == "on" ]]
  then
    # tput smam
    printf '\033[?7h'
  fi
}

# _list_files()
#
# Usage:
#   _list_files <notebook-path> [--sort] [--reverse] [--type <type>]
#
# Description:
#   List files in the current notebook.
_list_files() {
  local _notebook_path=
  local _reverse=0
  local _sort=0
  local _type=

  if [[ -z "${_notebook_path:-}" ]]
  then
    _notebook_path="$(_notebooks current --path)"
  fi

  while ((${#}))
  do
    local __arg="${1:-}"
    local __val="${2:-}"

    case "${__arg}" in
      --reverse)
        _reverse=1
        ;;
      --sort)
        _sort=1
        ;;
      --type)
        if _option_value_is_present "${__val:-}"
        then
          _type="${__val}"
          shift
        fi
        ;;
      *)
        if [[ -z "${_notebook_path:-}" ]]
        then
          _notebook_path="${__arg}"
        fi
    esac

    shift
  done

  if ((_sort))
  then
    cat "${_notebook_path}/.index"
  else
    ls -t -1 "${_notebook_path}"
  fi | {
    if ((_reverse))
    then
      # https://stackoverflow.com/a/744093
      sed '1!G;h;$!d'
    else
      cat
    fi
  } | {
    if [[ -n "${_type:-}" ]]
    then
      local _grep_patterns=()

      case "${_type}" in
        bookmark|bookmarks)
          grep --color=never    \
            -e '\.bookmark\.'   \
            -e '\-bookmark\.'
          ;;
        book|books)
          grep --color=never -e 'epub$'
          ;;
        note|notes)
          for __extension in "${_TEXT_FILE_EXTENSIONS[@]}"
          do
            _grep_patterns+=("-e" "${__extension}$")
          done

          grep --color=never "${_grep_patterns[@]}" \
            | grep --color=never -v \
                -e '\.bookmark\.'   \
                -e '\-bookmark\.'
          ;;
        folder|folders|directory|directories)
          while read -r __line
          do
            if [[ -d "${_notebook_path}/${__line}" ]]
            then
              printf "%s\\n" "${__line}"
            fi
          done
          ;;
        archive)
          for __extension in "${_ARCHIVE_FILE_EXTENSIONS[@]}"
          do
            _grep_patterns+=("-e" "${__extension}$" "-e" "${__extension}.enc$")
          done

          grep --color=never "${_grep_patterns[@]}"
          ;;
        audio|music)
          for __extension in "${_AUDIO_FILE_EXTENSIONS[@]}"
          do
            _grep_patterns+=("-e" "${__extension}$" "-e" "${__extension}.enc$")
          done

          grep --color=never "${_grep_patterns[@]}"
          ;;
        document|documents|doc|docs)
          for __extension in "${_DOCUMENT_FILE_EXTENSIONS[@]}"
          do
            _grep_patterns+=("-e" "\.${__extension}$" "-e" "\.${__extension}\.enc$")
          done

          grep --color=never "${_grep_patterns[@]}"
          ;;
        encrypted)
          for __extension in "${_ENCRYPTED_FILE_EXTENSIONS[@]}"
          do
            _grep_patterns+=("-e" "\.${__extension}$")
          done

          grep --color=never "${_grep_patterns[@]}"
          ;;
        image|images|picture|pictures)
          for __extension in "${_IMAGE_FILE_EXTENSIONS[@]}"
          do
            _grep_patterns+=("-e" "\.${__extension}$" "-e" "\.${__extension}\.enc$")
          done

          grep --color=never "${_grep_patterns[@]}"
          ;;
        text|txt)
          for __extension in "${_TEXT_FILE_EXTENSIONS[@]}"
          do
            _grep_patterns+=("-e" "\.${__extension}$")
          done

          grep --color=never "${_grep_patterns[@]}"
          ;;
        video|videos)
          for __extension in "${_VIDEO_FILE_EXTENSIONS[@]}"
          do
            _grep_patterns+=("-e" "\.${__extension}$" "-e" "\.${__extension}\.enc$")
          done

          grep --color=never "${_grep_patterns[@]}"
          ;;
        *)
          grep --color=never -e "\.${_type}$"
          ;;
      esac
    else
      cat
    fi
  }
}

# _pager()
#
# Usage:
#   _pager
#
# Description:
#   Display standard input in the preferred pager, `less`, or `cat`.
_pager() {
  if [[ -n "${PAGER:-}" ]] && [[ "${PAGER:-}" =~ less ]]
  then
    "${PAGER}" -R --CLEAR-SCREEN --prompt="$(_less_prompt)"
  elif _command_exists "less"
  then
    less -R --CLEAR-SCREEN --prompt="$(_less_prompt)"
  elif [[ -n "${PAGER:-}" ]]
  then
    "${PAGER}"
  else
    cat
  fi
}

# _print_line()
#
# Usage:
#   _print_line <text>
#
# Description:
#   Print a line of dashes the length of <text>.
#
# More Information:
#   https://wiki.bash-hackers.org/commands/builtin/printf
_print_line() {
  local _text="${1:-}"
  local _text_length="${#_text}"
  local _line=
  printf -v _line "%*s" "${_text_length}" ""
  _color_secondary "${_line// /-}"
}

# _print_padding()
#
# Usage:
#   _print_padding <line> <columns> [<aligned>]
#
# Description:
#   Print a string of spaces that can be used as left padding to
#   center-align <line>. <aligned> is an optional boolean that can
#   be used to print no padding, returning an empty string.
_print_padding() {
  local _padding_line="${1:-}"
  local _padding_line_length="${#_padding_line}"
  local _padding_columns="${2:-}"
  local _padding_centered="${3:-1}"

  local _padding_amount=$(( (_padding_columns-_padding_line_length) / 2 ))

  if ((_padding_centered)) && ((_padding_amount))
  then
    printf "%-${_padding_amount}s" " "
  fi
}

# _print_welcome()
#
# Usage:
#   _print_welcome
#
# Description:
#   Print the welcome message.
_print_welcome() {
  local _columns
  _columns="$(tput cols)"

  local _padding
  _padding="$(_print_padding " / / | | | | |_) |" "${_columns}")"

  _print_line "$(printf "%-${_columns}s" '.')"

  cat <<HEREDOC
${_padding}$(_color_primary "Welcome") to
${_padding}__          _
${_padding}\ \   _ __ | |__
${_padding} \ \ | '_ \| '_ \\
${_padding} / / | | | | |_) |
${_padding}/_/  |_| |_|_.__/
HEREDOC

  _print_line "$(printf "%-${_columns}s" '.')"
}

# _spinner()
#
# Usage:
#   _spinner <pid>
#
# Description:
#   Display an ascii spinner while <pid> is running.
#
# Example Usage:
#   ```
#   _spinner_example() {
#     printf "Working..."
#     (sleep 1) &
#     _spinner $!
#     printf "Done!\\n"
#   }
#   (_spinner_example)
#   ```
#
# More Information:
#   http://fitnr.com/showing-a-bash-spinner.html
_spinner() {
  local _delay=0.75
  local _pid="${1:-}"
  local _spin_string="|/-\\"

  if [[ -z "${_pid}" ]]
  then
    _exit_1 printf "Usage: _spinner <pid>\\n"
  fi

  while ps a | awk '{print $1}' | grep -q "${_pid}"
  do
    local _temp="${_spin_string#?}"
    printf " [%c]  " "${_spin_string}"
    _spin_string="${_temp}${_spin_string%${_temp}}"
    sleep ${_delay}
    printf "\b\b\b\b\b\b"
  done
  printf "    \b\b\b\b"
}

# _string_is_email()
#
# Usage:
#   _string_is_email <string>
#
# Exit / Error Status:
#   0 (success, true)  If the string is recognized as an email address.
#   1 (error,  false)  if not.
_string_is_email() {
  [[ "${1:-}" =~ ^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$ ]]
}

# _string_is_url()
#
# Usage:
#   _string_is_url <string>
#
# Exit / Error Status:
#   0 (success, true)  If the string is a recognized URL.
#   1 (error,  false)  if not.
_string_is_url() {
  [[ "${1:-}" =~ ^https\:\/\/|^http\:\/\/|^file\:\/\/|^ftp\:\/\/|^sftp\:\/\/ ]]
}

###############################################################################
# describe
###############################################################################

# describe()
#
# Usage:
#   describe <name> <description>
#   describe --get <name>
#
# Options:
#   --get  Print the description for <name> if one has been set.
#
# Examples:
# ```
#   describe "list" <<HEREDOC
# Usage:
#   ${_ME} list
#
# Description:
#   List items.
# HEREDOC
#
# describe --get "list"
# ```
#
# Set or print a description for a specified command or function <name>. The
# <description> text can be passed as the second argument or as standard input.
#
# To make the <description> text available to other functions, `describe()`
# assigns the text to a variable with the format `$____describe_<name>`.
#
# When the `--get` option is used, the description for <name> is printed, if
# one has been set.
describe() {
  set +e
  [[ -z "${1:-}" ]] &&
    _exit_1 printf "describe(): <name> required.\\n"

  if [[ "${1}" == "--get" ]]
  then # get ------------------------------------------------------------------
    [[ -z "${2:-}" ]] &&
      _exit_1 printf "describe(): <description> required.\\n"

    local _name="${2:-}"
    local _describe_var="____describe_${_name}"

    if [[ ! "${_name}" =~ ^\- ]] && [[ -n "${!_describe_var:-}" ]]
    then
      printf "%s\\n" "${!_describe_var}"
    else
      printf "No additional information for \`%s\`\\n" "${_name}"
    fi
  else # set ------------------------------------------------------------------
    if [[ -n "${2:-}" ]]
    then # argument is present
      read -r -d '' "____describe_${1}" <<HEREDOC
${2}
HEREDOC
    else # no argument is present, so assume piped input
      read -r -d '' "____describe_${1}"
    fi
  fi
  set -e
}

# Support legacy `desc` name.
desc() { describe "${@}"; }

###############################################################################
# help
###############################################################################

describe "help" <<HEREDOC
Usage:
  ${_ME} help [<subcommand>] [-p | --print]
  ${_ME} help [-c | --colors] | [-r | --readme] | [-s | --short] [-p | --print]

Options:
  -c, --colors  View information about color themes and color settings.
  -p, --print   Print to standard output / terminal.
  -r, --readme  View the \`${_ME}\` README file.
  -s, --short   Print shorter help without subcommand descriptions.

Description:
  Print the program help information. When a subcommand name is passed, print
  the help information for the subcommand.

Examples:
  ${_ME} help
  ${_ME} help add
  ${_ME} help import
  ${_ME} h notebooks
  ${_ME} h e

Shortcut Alias: \`h\`
HEREDOC
_help() {
  local _arguments=()
  local _color_help=0
  local _readme=0
  local _shell=0
  local _short=0
  local _use_pager=1

  for __arg in "${@:-}"
  do
    case "${__arg}" in
      --pager)
        _use_pager=1
        ;;
      -p|--print|--dump|--skip*pager|--no*pager)
        _use_pager=0
        ;;
      -r|*readme*|*README*)
        _readme=1
        ;;
      --shell)
        _shell=1
        ;;
      -s|--short)
        _short=1
        ;;
      -c|*color*|*colour*|*theme*)
        _color_help=1
        ;;
      *)
        _arguments+=("${__arg}")
        ;;
    esac
  done

  if ((_readme))
  then
    _download_from \
      "${_REPO_RAW_URL}/README.md" \
      | if _command_exists "pandoc" && _browser
        then
          pandoc --from markdown --to html | _browser
          return 0
        else
          _highlight_syntax_if_available
        fi
  elif ((_color_help))
  then
    # NOTE: this screen mirrors the content from README.md#-color-themes:
    # https://github.com/xwmx/nb#-color-themes
    _color_primary "__          _                     _"
    _color_primary "\ \   _ __ | |__         ___ ___ | | ___  _ __ ___"
    _color_primary " \ \ | '_ \| '_ \   _   / __/ _ \| |/ _ \| '__/ __|"
    _color_primary " / / | | | | |_) | (_) | (_| (_) | | (_) | |  \__ \\"
    _color_primary "/_/  |_| |_|_.__/       \___\___/|_|\___/|_|  |___/"
    _color_secondary "\
---------------------------------------------------"
  cat <<HEREDOC
\`${_ME}\` uses color to highlight various interface elements, including ids,
the current notebook name, the shell prompt, and divider lines.

\`${_ME}\` includes several built-in color themes and also supports user-defined
themes. The current color theme can be set using \`${_ME} set color_theme\`:

    $(_color_primary "${_ME} set color_theme")

Custom color themes are \`nb\` plugins with a \`.nb-theme\` file extension.
A theme is defined with a single \`if\` statement indicating the name and
assigning the color environment variables to \`tput\` ANSI color numbers:

    $(_color_secondary "# turquoise.nb-theme")
    $(_color_primary "if [[ \"\${NB_COLOR_THEME}\" == ${_TPUT_SGR0}\"turquoise\"${_TPUT_COLOR_PRIMARY} ]]")
    $(_color_primary "then")
    $(_color_primary "  export NB_COLOR_PRIMARY=${_TPUT_SGR0}43${_TPUT_COLOR_PRIMARY}")
    $(_color_primary "  export NB_COLOR_SECONDARY=${_TPUT_SGR0}38${_TPUT_COLOR_PRIMARY}")
    $(_color_primary "fi")

View this theme as a complete file:

    $(_color_primary "https://github.com/xwmx/nb/blob/master/plugins/turquoise.nb-theme")

Themes can be installed using the \`${_ME} plugins\` subcommand:

    $(_color_dim ">") $(_color_primary "${_ME} plugins install https://github.com/xwmx/nb/blob/master/plugins/turquoise.nb-theme")
    $(_color_secondary "Plugin installed:")
    $(_color_secondary "/home/example/.nb/.plugins/turquoise.nb-theme")

Once a theme is installed, use it with \`${_ME} set color_theme\`:

    $(_color_dim ">") $(_color_primary "set color_theme turquoise")
    $(_color_secondary "NB_COLOR_THEME set to turquoise")

The primary and secondary colors can also be overridden individually,
making color themes easily customizable:

    $(_color_secondary "# open the settings prompt for the primary color")
    $(_color_primary "${_ME} set color_primary")

    $(_color_secondary "# open the settings prompt for the secondary color")
    $(_color_primary "${_ME} set color_secondary")

To view a table of available colors and numbers, run:

    $(_color_primary "${_ME} settings colors")

To set the syntax highlighting color theme, use:

    $(_color_primary "${_ME} set syntax_theme")

HEREDOC
  elif [[ -z "${_arguments[*]:-}" ]] || ((_short))
  then
    _color_primary    "__          _"
    _color_primary    "\ \   _ __ | |__"
    _color_primary    " \ \ | '_ \| '_ \\"
    _color_primary    " / / | | | | |_) |"
    _color_primary    "/_/  |_| |_|_.__/"
    _color_secondary  "------------------"
   cat <<HEREDOC

$(_color_brackets "nb") Command line note-taking, bookmarking, archiving with plain-text data
storage, encryption, filtering and search, Git-backed versioning and syncing,
Pandoc-backed conversion, global and local notebooks, customizable color
themes, plugins, and more in a single portable, user-friendly script.

$(_color_primary "Help:")
  ${_ME} help               Display this help information.
  ${_ME} help <subcommand>  View help information for <subcommand>.
  ${_ME} help --colors      View information about color settings.
  ${_ME} help --readme      View the \`${_ME}\` README file.

$(_color_primary "Usage:")
  ${_ME}
  ${_ME} [<ls options>...] [<id> | <filename> | <path> | <title> | <notebook>]
  ${_ME} [<url>] [<bookmark options>...]
  ${_ME} add [<filename> | <content>] [-c <content> | --content <content>]
         [--edit] [-e | --encrypt] [-f <filename> | --filename <filename>]
         [-t <title> | --title <title>] [--type <type>]
  ${_ME} bookmark [<ls options>...]
  ${_ME} bookmark <url> [-c <comment> | --comment <comment>] [--edit]
              [-e | --encrypt] [-f <filename> | --filename <filename>]
              [-q | --quote] [-r <url> | --related <url>]... [--save-source]
              [--skip-content] [-t <tag1>,<tag2>... | --tags <tag1>,<tag2>...]
              [--title <title>]
  ${_ME} bookmark [list [<list-options>...]]
  ${_ME} bookmark (open | peek | url) (<id> | <filename> | <path> | <title>)
  ${_ME} bookmark (edit | delete) (<id> | <filename> | <path> | <title>)
  ${_ME} bookmark search <query>
  ${_ME} completions (check | install [-d | --download] | uninstall)
  ${_ME} count
  ${_ME} delete (<id> | <filename> | <path> | <title>) [-f | --force]
  ${_ME} edit (<id> | <filename> | <path> | <title>)
          [-c <content> | --content <content>] [--edit]
          [-e <editor> | --editor <editor>]
  ${_ME} export (<id> | <filename> | <path> | <title>) <path> [-f | --force]
            [<pandoc options>...]
  ${_ME} export notebook <name> [<path>]
  ${_ME} export pandoc (<id> | <filename> | <path> | <title>)
            [<pandoc options>...]
  ${_ME} git [checkpoint [<message>] | dirty]
  ${_ME} git <git-options>...
  ${_ME} help [<subcommand>] [-p | --print]
  ${_ME} help [-c | --colors] | [-r | --readme] | [-s | --short] [-p | --print]
  ${_ME} history [<id> | <filename> | <path> | <title>]
  ${_ME} import [copy | download | move] (<path>... | <url>) [--convert]
  ${_ME} import notebook <path> [<name>]
  ${_ME} init [<remote-url>]
  ${_ME} list [-e [<length>] | --excerpt [<length>]] [--filenames]
          [-n <limit> | --limit <limit> |  --<limit>] [--no-id]
          [--no-indicator] [-p | --pager] [--paths] [-s | --sort]
          [-r | --reverse] [-t <type> | --type <type> | --<type>]
          [<id> | <filename> | <path> | <title> | <query>]
  ${_ME} ls [-a | --all] [-e [<length>] | --excerpt [<length>]] [--filenames]
        [-n <limit> | --limit <limit> | --<limit>] [--no-id] [--no-indicator]
        [-p | --pager] [--paths] [-s | --sort] [-r | --reverse]
        [-t <type> | --type <type> | --<type>]
        [<id> | <filename> | <path> | <title> | <query>]
  ${_ME} move (<id> | <filename> | <path> | <title>) [-f | --force] <notebook>
  ${_ME} notebooks [<name>] [--archived] [--global] [--local] [--names]
               [--paths] [--unarchived]
  ${_ME} notebooks add <name> [<remote-url>]
  ${_ME} notebooks (archive | open | peek | status | unarchive) [<name>]
  ${_ME} notebooks current [--path | --selected | --filename [<filename>]]
                       [--global | --local]
  ${_ME} notebooks delete <name> [-f | --force]
  ${_ME} notebooks (export <name> [<path>] | import <path>)
  ${_ME} notebooks init [<path> [<remote-url>]]
  ${_ME} notebooks rename <old-name> <new-name>
  ${_ME} notebooks select <selector>
  ${_ME} notebooks show (<name> | <path> | <selector>) [--archived]
                    [--escaped | --name | --path | --filename [<filename>]]
  ${_ME} notebooks use <name>
  ${_ME} open (<id> | <filename> | <path> | <title> | <notebook>)
  ${_ME} peek (<id> | <filename> | <path> | <title> | <notebook>)
  ${_ME} plugins [<name>] [--paths]
  ${_ME} plugins install [<path> | <url>] [--force]
  ${_ME} plugins uninstall <name> [--force]
  ${_ME} remote [remove | set <url> [-f | --force]]
  ${_ME} rename (<id> | <filename> | <path> | <title>) [-f | --force]
            (<name> | --reset | --to-bookmark | --to-note)
  ${_ME} run <command> [<arguments>...]
  ${_ME} search <query> [-a | --all] [-t <type> | --type <type> | --<type>]
                    [-l | --list] [--path]
  ${_ME} set [<name> [<value>] | <number> [<value>]]
  ${_ME} settings [colors [<number> | themes] | edit | list [--long]]
  ${_ME} settings (get | show | unset) (<name> | <number>)
  ${_ME} settings set (<name> | <number>) <value>
  ${_ME} shell [<subcommand> [<options>...] | --clear-history]
  ${_ME} show (<id> | <filename> | <path> | <title>) [[-a | --added] |
          --filename | --id | --info-line | --path | [-p | --print]
          [-r | --render] | --selector-id | --title | --type [<type>] |
          [-u | --updated]]
  ${_ME} show <notebook>
  ${_ME} subcommands [add <name>...] [alias <name> <alias>]
                 [describe <name> <usage>]
  ${_ME} sync [-a | --all]
  ${_ME} update
  ${_ME} use <notebook>
  ${_ME} -i | --interactive [<subcommand> [<options>...]]
  ${_ME} -h | --help | help [<subcommand> | --readme]
  ${_ME} --no-color
  ${_ME} --version | version
HEREDOC

    if ! ((_short))
    then
      cat <<HEREDOC

$(_color_primary "Subcommands:")
  (default)    List notes and notebooks. This is an alias for \`${_ME} ls\`.
               When a <url> is provided, create a new bookmark.
  add          Add a new note.
  bookmark     Add, open, list, and search bookmarks.
  completions  Install and uninstall completion scripts.
  count        Print the number of notes.
  delete       Delete a note.
  edit         Edit a note.
  export       Export a note to a variety of different formats.
  git          Run \`git\` commands within the current notebook.
  help         View help information for the program or a subcommand.
  history      View git history for the current notebook or a note.
  import       Import a file into the current notebook.
  init         Initialize the first notebook.
  list         List notes in the current notebook.
  ls           List notebooks and notes in the current notebook.
  move         Move a note to a different notebook.
  notebooks    Manage notebooks.
  open         Open a bookmarked web page or notebook folder, or edit a note.
  peek         View a note, bookmarked web page, or notebook in the terminal.
  plugins      Install and uninstall plugins and themes.
  remote       Get, set, and remove the remote URL for the notebook.
  rename       Rename a note.
  run          Run shell commands within the current notebook.
  search       Search notes.
  settings     Edit configuration settings.
  shell        Start the \`${_ME}\` interactive shell.
  show         Show a note or notebook.
  status       Run \`git status\` in the current notebook.
  subcommands  List, add, alias, and describe subcommands.
  sync         Sync local notebook with the remote repository.
  update       Update \`${_ME}\` to the latest version.
  use          Switch to a notebook.
  version      Display version information.

$(_color_primary "Notebook Usage:")
  ${_ME} <notebook>:[<subcommand>] [<identifier>] [<options>...]
  ${_ME} <subcommand> <notebook>:<identifier> [<options>...]

$(_color_primary "Program Options:")
  -i, --interactive   Start the \`${_ME}\` interactive shell.
  -h, --help          Display this help information.
  --no-color          Print without color highlighting.
  --version           Display version information.

$(_color_primary "More Information:")
  https://github.com/${_REPO}
HEREDOC
    fi
  else
    printf "%s\\n" "$(describe --get "${_arguments[@]}")"
  fi |  if ((_shell))
        then
          sed -e "s/^  nb /  /" -e "/^  nb$/d"
        else
          cat
        fi |  if ((_use_pager))
              then
                _pager
              else
                cat
              fi
}
_alias_subcommand "help" "h"

###############################################################################
# Subcommands
###############################################################################

# add ##################################################################### add

describe "add" <<HEREDOC
Usage:
  ${_ME} add [<filename> | <content>] [-c <content> | --content <content>]
         [--edit] [-e | --encrypt] [-f <filename> | --filename <filename>]
         [-t <title> | --title <title>] [--type <type>]

Options:
  -c, --content <content>     The content for the new note.
  --edit                      Open the note in the editor before saving when
                              content is piped or passed as an argument.
  -e, --encrypt               Encrypt the note with a password.
  -f, --filename <filename>   The filename for the new note. The default
                              extension is used when the extension is omitted.
  -t, --title <title>         The title for a new note. If \`--title\` is
                              present, the filename will be derived from the
                              title, unless \`--filename\` is specified.
  --type <type>               The file type for the new note, as a file
                              extension.

Description:
  Create a new note.

  If no arguments are passed, a new blank note file is opened with
  \`\$EDITOR\`, currently set to "${EDITOR}". If a non-option argument is
  passed, \`${_ME}\` will treat it as a <filename≥ if a file extension is found.
  If no file extension is found, \`${_ME}\` will treat the string as
  <content> and will create a new note without opening the editor.
  \`${_ME} add\` can also create a new note with piped content.

  \`${_ME}\` creates Markdown files by default. To create a note with a
  different file type, use the extension in the filename or use the \`--type\`
  option. To change the default file type, use \`${_ME} set default_extension\`.

  When the \`-e\` / \`--encrypt\` option is used, \`${_ME}\` will encrypt the
  note with AES-256 using OpenSSL by default, or GPG, if configured in
  \`${_ME} set encryption_tool\`.

Examples:
  ${_ME} add
  ${_ME} add example.md
  ${_ME} add "Note content."
  ${_ME} add example.md --title "Example Title" --content "Example content."
  echo "Note content." | ${_ME} add
  ${_ME} add -t "Secret Document" --encrypt
  ${_ME} example:add
  ${_ME} example:add -t "Title"
  ${_ME} a
  ${_ME} a "Note content."
  ${_ME} example:a
  ${_ME} example:a -t "Title"

Aliases: \`create\`, \`new\`
Shortcut Alias: \`a\`
HEREDOC
_add() {
  local _arguments=()
  local _basename=
  local _content=
  local _edit_before_commit=0
  local _encrypt=0
  local _file_type="${NB_DEFAULT_EXTENSION}"
  local _maybe_content_or_filename=
  local _password=
  local _target_filename=
  local _title=

  while ((${#}))
  do
    local __arg="${1:-}"
    local __val="${2:-}"

    case "${__arg}" in
      -c|--content)
        _content="$(_option_get_value "${__arg}" "${__val:-}")"
        shift
        ;;
      --edit)
        _edit_before_commit=1
        ;;
      -e|--encrypt*)
        if [[ -z "${NB_ENCRYPTION_TOOL:-}" ]] ||
           ! _command_exists "${NB_ENCRYPTION_TOOL:-}"
        then
          _exit_1 printf                        \
            "Encryption tool not found: %s\\n"  \
            "$(_color_primary "${NB_ENCRYPTION_TOOL:-}")"
        else
          _encrypt=1
        fi
        ;;
      -f|--filename)
        _target_filename="$(_option_get_value "${__arg}" "${__val:-}")"
        shift
        ;;
      --password)
        _password="$(_option_get_value "${__arg}" "${__val:-}")"
        shift
        ;;
      -t|--title|--name)
        _title="$(_option_get_value "${__arg}" "${__val:-}")"
        shift
        ;;
      --type)
        _file_type="$(_option_get_value "${__arg}" "${__val:-}")"
        shift
        ;;
      *)
        if [[ -z "${_maybe_content_or_filename:-}" ]]
        then
          _maybe_content_or_filename="${__arg:-}"
        else
          _arguments+=("${__arg:-}")
        fi
        ;;
    esac

    shift
  done

  local _common_non_text_file_extension_tlds=(
    app agency au com company biz blog buzz ca cloud club de design dev cf ch
    cn edu email es fr fun ga gov guru icu info it jp link life live ly media
    mil net new nl no online nyc page rocks ru se shop site solutions space
    store tech top tk tv uk us vip wang website work xyz
  )

  local _maybe_content_or_filename_extension="${_maybe_content_or_filename##*\.}"

  if [[ -n "${_maybe_content_or_filename:-}" ]]
  then
    if  [[ "${_maybe_content_or_filename:-}" =~ \.[A-Za-z0-9]+$   ]]  &&
        [[ -n "${_maybe_content_or_filename_extension:-}"         ]]  &&
        [[ -z "${_target_filename}"                               ]]  &&
        ! _string_is_url "${_maybe_content_or_filename:-}"            &&
        ! _string_is_email "${_maybe_content_or_filename:-}"          &&
        ! _contains "${_maybe_content_or_filename_extension:-}"   \
                    "${_common_non_text_file_extension_tlds[@]}"
    then
      _target_filename="${_maybe_content_or_filename}"
    else
      _content="${_maybe_content_or_filename}"
    fi
  fi

  if [[ -n "${_target_filename:-}" ]]
  then
    _basename="$(_notebooks current --filename "${_target_filename:-}")"
  elif [[ -n "${_title:-}" ]]
  then
    _target_filename="$(
      printf "%s" "${_title}"           \
        | tr '[:upper:]' '[:lower:]'    \
        | tr '[:space:]:*/"\\<>?|' '_'
    )"
    _basename="$(_notebooks current --filename "${_target_filename}.${_file_type}")"
  else
    _basename="$(_notebooks current --filename ".${_file_type}")" # !! note the '.'
  fi

  local _notebook_path
  _notebook_path="$(_notebooks current --path)"

  _target_path="${_notebook_path}/${_basename}"

  if ((_encrypt))
  then
    if [[ -z "${_password}" ]]
    then
      # request password without displaying it
      printf "%s: " "$(_color_primary "Password")"
      read -r -s _password </dev/tty
      printf "\\n" # print newline to stop `read`

      if [[ -z "${_password:-}" ]]
      then
        _exit_1 printf "Password required.\\n"
      fi

      local _password_confirmation=

      # request password without displaying it
      printf "%s: " "$(_color_primary "Confirm Password")"
      read -r -s _password_confirmation </dev/tty
      printf "\\n" # print newline to stop `read`

      if [[ "${_password}" != "${_password_confirmation}" ]]
      then
        _exit_1 printf "Password mismatch.\\n"
      fi
    fi

    _target_path="$(_temp file "${_basename}")"
  fi

  if [[ -n "${_title}" ]]
  then
    printf "%s\\n\\n" "# ${_title}" >> "${_target_path}"
  fi

  if [[ -n "${_content}" ]]
  then
    printf "%s\\n" "${_content}" >> "${_target_path}"
  fi

  if ! _interactive_input
  then # piped input
    cat >> "${_target_path}"
  fi

  if ((_edit_before_commit)) || {
    _interactive_input && [[ -z "${_content:-}" ]]
  }
  then
    _edit_file "${_target_path}"
  fi

  if ((_encrypt)) && [[ -e "${_target_path}" ]]
  then
    local _decrypted_path="${_target_path}"
    local _encrypted_path="${_notebook_path}/${_basename}.enc"

    _encrypt_file "${_decrypted_path}" "${_encrypted_path}" "${_password}"

    if [[ -n "${_decrypted_path}" ]] && [[ -e "${_decrypted_path}" ]]
    then
      rm "${_decrypted_path:?}"
    fi

    _basename="${_basename}.enc"
    _target_path="${_encrypted_path}"
  fi

  if [[ -e "${_target_path}" ]]
  then
    local _info
    _info="$(_show "${_basename}" --info-line)"

    printf "Added: %s\\n" "${_info}"
  fi &&
    _git checkpoint "${_notebook_path}" "[nb] Add: ${_basename}"
}
_alias_subcommand "add" "a"
_alias_subcommand "add" "create"
_alias_subcommand "add" "new"

# bookmark ########################################################### bookmark

describe "bookmark" <<HEREDOC
Usage:
  ${_ME} bookmark [<ls options>...]
  ${_ME} bookmark <url> [-c <comment> | --comment <comment>] [--edit]
              [-e | --encrypt] [-f <filename> | --filename <filename>]
              [-q | --quote] [-r <url> | --related <url>]... [--save-source]
              [--skip-content] [-t <tag1>,<tag2>... | --tags <tag1>,<tag2>...]
              [--title <title>]
  ${_ME} bookmark list [<list-options>...]
  ${_ME} bookmark (open | peek | url) (<id> | <filename> | <path> | <title>)
  ${_ME} bookmark (edit | delete) (<id> | <filename> | <path> | <title>)
  ${_ME} bookmark search <query>

Options:
  -c, --comment <comment>      A comment or description for this bookmark.
  --edit                       Open the bookmark in your editor before saving.
  -e, --encrypt                Encrypt the bookmark with a password.
  -f, --filename <filename>    The filename for the bookmark. It is
                               recommended to omit the extension so the
                               default bookmark extension is used.
  -q, --quote <quote>          A quote or excerpt from the saved page.
                               Alias: \`--excerpt\`
  -r, --related <url>          A URL for a page related to the bookmarked page.
                               Multiple \`--related\` flags can be used in a
                               command to save multiple related URLs.
  --save-source                Save the page source as HTML.
  --skip-content               Omit page content from the note.
  -t, --tags <tag1>,<tag2>...  A comma-separated list of tags.
  --title <title>              The bookmark title. When not specified,
                               \`${_ME}\` will use the html <title> tag.

Subcommands:
  (default)  Add a new bookmark for <url>, or list bookmarks.
             Bookmarks can also be added with \`${_ME} <url>\`
  delete     Delete a bookmark.
  edit       Edit a bookmark.
  list       List bookmarks in the current notebook.
             Shortcut Alias: \`ls\`
  open       Open the bookmarked page in your system's primary web browser.
             Shortcut Alias: \`o\`
  peek       Open the bookmarked page in your terminal web browser.
             Alias: \`preview\`
             Shortcut Alias: \`p\`
  search     Search bookmarks for <query>.
             Shortcut Alias: \`q\`
  url        Print the URL for the specified bookmark.

Description:
  Create, view, search, edit, and delete bookmarks.

  By default, the html page content is saved within the bookmark, making the
  bookmarked page available for full-text search. When Pandoc [1] is
  installed, the HTML content will be converted to Markdown before saving.
  When readability-cli [2] is install, markup is cleaned up to focus on
  content.

  \`peek\` opens the page in \`w3m\` [3] or \`lynx\` [4] when available.
  To specify a preferred browser, set the \`\$BROWSER\` environment variable
  in your .bashrc, .zshrc, or equivalent, e.g., \`export BROWSER="lynx"\`.

  Bookmarks are identified by the \`.bookmark.md\` file extension. The
  bookmark URL is the first URL in the file within "<" and ">" characters:

    <https://www.example.com>

    1. https://pandoc.org/
    2. https://gitlab.com/gardenappl/readability-cli
    3. https://en.wikipedia.org/wiki/W3m
    4. https://en.wikipedia.org/wiki/Lynx_(web_browser)

Examples:
  ${_ME} https://example.com
  ${_ME} example: https://example.com
  ${_ME} https://example.com --encrypt
  ${_ME} https://example.com --tags example,sample,demo
  ${_ME} https://example.com/about -c "Example comment."
  ${_ME} https://example.com/faqs -f example-filename
  ${_ME} https://example.com --quote "Example quote or excerpt."
  ${_ME} bookmark list
  ${_ME} bookmark search "example query"
  ${_ME} bookmark open 5
  ${_ME} b

Shortcut Alias: \`b\`
HEREDOC
_bookmark() {
  # Usage: _bookmark_view_in_terminal_browser (<url>)
  _bookmark_view_in_terminal_browser() {
    local _target_url="${1:-}"

    if _browser
    then
      _browser "${_target_url}"
      return 0
    elif _command_exists "curl" || _command_exists "wget"
    then
      _download_from "${_target_url}" \
        | if _command_exists "pandoc"
          then
            pandoc --from html-native_divs-native_spans --to markdown \
              | _highlight_syntax_if_available "md"
          else
            cat
          fi | _pager

      return 0
    else
      _exit_1 printf "Terminal web browser not detected.\\n"
    fi
  }

  local _arguments=()
  local _bookmark_content=
  local _comment=
  local _edit_before_commit=0
  local _encrypt=0
  local _password=
  local _quote=
  local _related_urls=()
  local _save_source=0
  local _selector=
  local _skip_content=0
  local _subcommand=
  local _tag_list=
  local _target_filename=
  local _title=
  local _url=

  while ((${#}))
  do
    local __arg="${1:-}"
    local __val="${2:-}"

    case "${__arg}" in
      -c|--comment*)
        _comment="$(_option_get_value "${__arg}" "${__val:-}")"
        shift
        ;;
      --edit)
        _edit_before_commit=1
        ;;
      -e|--encrypt*)
        _encrypt=1
        ;;
      -f|--filename)
        _target_filename="$(_option_get_value "${__arg}" "${__val:-}")"
        shift
        ;;
      --password)
        _password="$(_option_get_value "${__arg}" "${__val:-}")"
        shift
        ;;
      -q|--quote|--excerpt)
        _quote="$(_option_get_value "${__arg}" "${__val:-}")"
        shift
        ;;
      -r|--via|--also|--related)
        _related_urls+=("$(_option_get_value "${__arg}" "${__val:-}")")
        shift
        ;;
      --save*source|--source|--save*html|--html|--raw*content|--raw*html)
        _save_source=1
        ;;
      --skip*content|--no*content)
        _skip_content=1
        ;;
      -t|--tag*)
        _tag_list="$(_option_get_value "${__arg}" "${__val:-}")"
        shift
        ;;
      --title)
        _title="$(_option_get_value "${__arg}" "${__val:-}")"
        shift
        ;;
      delete)
        _delete "${__val:-}" "${3:-}"
        return 0
        ;;
      edit)
        _edit "${__val:-}"
        return 0
        ;;
      help)
        _help "bookmark"
        return 0
        ;;
      ls|list)
        _subcommand="list"
        ;;
      search|q)
        _subcommand="search"
        ;;
      open|o|peek|p|preview|url)
        if _option_value_is_present "${__val:-}"
        then
          if [[ "${__arg}" =~ ^open$|^o$ ]]
          then
            _subcommand="open"
          elif [[ "${__arg}" =~ ^peek$|^p$|^preview$ ]]
          then
            _subcommand="peek"
          elif [[ "${__arg}" == "url" ]]
          then
            _subcommand="url"
          fi

          _selector="${__val:-}"

          shift
        else
          _exit_1 \
            printf "%s requires a valid argument.\\n"  \
            "$(_color_primary "bookmark ${__arg}")"
        fi
        ;;
      *)
        if [[ ! "${_subcommand:-}" =~ ^list$|^search$ ]] &&
           [[ -z "${_url:-}"                          ]] &&
           _string_is_url "${__arg}"
        then
          _url="${__arg}"
        elif [[ -z "${_subcommand:-}"               ]] ||
             [[ "${_subcommand}" =~ ^list$|^search$ ]]
        then
          _arguments+=("${__arg}")
        fi
        ;;
    esac

    shift
  done

  # `bookmark list`
  if [[ "${_subcommand}" == "list" ]]
  then
    if [[ -n "${_arguments[*]:-}" ]]
    then
      _list --bookmarks "${_arguments[@]}"
      return 0
    else
      _list --bookmarks
      return 0
    fi
  fi

  # `bookmark search`
  if [[ "${_subcommand}" == "search" ]]
  then
    _search --bookmarks "${_arguments[@]}"
    return 0
  fi

  # `bookmark open`, `bookmark peek`, `bookmark url`
  if [[ "${_subcommand}" =~ ^open$|^peek$|^url$ ]]
  then
    local _basename
    _basename="$(_show "${_selector}" --filename)"

    local _notebook_path
    _notebook_path="$(_notebooks current --path)"

    if [[ -z "${_basename}" ]]
    then
      if [[ "${_subcommand}" =~ ^open$|^peek$ ]]
      then
        local _maybe_notebook="${_selector:-}"
        _maybe_notebook="$(printf "%s\\n" "${_maybe_notebook}" | sed 's/\:$//')"

        if [[ -d "${NB_DIR}/${_maybe_notebook}/.git" ]]
        then
          _notebooks "${_subcommand}" "${_maybe_notebook}" && return 0
        fi
      fi
      _exit_1 printf "Not found: %s\\n" "$(_color_primary "${_selector}")"
    fi

    local _target_path="${_notebook_path}/${_basename}"

    # handle non-bookmarks
    if [[ "${_subcommand}" =~ ^open$|^peek$ ]]
    then
      if [[ "${_basename}" =~ \.html$ ]]
      then
        if [[ "${_subcommand}" == "open"  ]]
        then
          if _command_exists "xdg-open"
          then
            xdg-open "${_target_path}" && return 0
          elif [[ "${OSTYPE}" =~ ^darwin ]]
          then
            open "${_target_path}" && return 0
          fi
        elif [[ "${_subcommand}" == "peek" ]]
        then
          _bookmark_view_in_terminal_browser "file://${_target_path}"
          return 0
        fi
      elif [[ ! "${_basename}" =~ .bookmark\.md$      ]] &&
           [[ ! "${_basename}" =~ .bookmark\.md\.enc$ ]]
      then
        if [[ "${_subcommand}" == "open" ]]
        then
          if [[ -d "${_target_path}" ]] &&
             _command_exists "xdg-open"
          then
            xdg-open "${_target_path}" && return 0
          elif [[ -d "${_target_path}"    ]] &&
               [[ "${OSTYPE}" =~ ^darwin  ]]
          then
            open "${_target_path}" && return 0
          elif [[ -n "${_password}" ]]
          then
            _edit "${_selector}" --password "${_password}"
          else
            _edit "${_selector}"
          fi
        elif [[ "${_subcommand}" == "peek" ]]
        then
          if [[ -n "${_password}" ]]
          then
            _show "${_selector}" --password "${_password}"
          else
            _show "${_selector}"
          fi
        fi

        return 0
      fi
    fi

    if _file_is_encrypted "${_target_path}"
    then
      local _encrypted_path=
      _encrypted_path="${_target_path}"

      if [[ -z "${_password}" ]]
      then
        # request password without displaying it
        printf "%s: " "$(_color_primary "Password")"
        read -r -s _password </dev/tty
        printf "\\n" # print newline to stop `read`
      fi

      if [[ -z "${_password}" ]]
      then
        _exit_1 printf "Password required.\\n"
      fi

      _target_path="$(_decrypt_file "${_target_path}" "${_password}")"
    fi

    local _target_url=
    _target_url="$(
      awk 'match($0, /<((ftp|http|file|mailto|news|telnet|gopher).*)>/) {
        print substr($0, RSTART+1, RLENGTH-2); exit
      }' "${_target_path}"
    )"

    if [[ "${_subcommand}" =~ ^open$|^peek$ ]]
    then
      local _check_wayback=0
      local _http_error_codes=(
        404 408 410 451 500 502 503 504 509 520 521 523 524 525 526
      )
      local _http_status=
      _http_status="$(_get_http_status "${_target_url}")"

      if _contains "${_http_status}" "${_http_error_codes[@]}"
      then
        printf "Page no longer available: %s\\n" \
          "$(_color_primary "${_target_url}")"
        while true
        do
          IFS='' read -r -e -d $'\n' -p "\
Check the Wayback Machine for an archived version instead? $(_color_brackets "y/N") " __yn

          case ${__yn} in
            [Yy]*)
              _check_wayback=1
              break
              ;;
            *)
              break
              ;;
          esac
        done
      fi

      if ((_check_wayback))
      then
        local _wayback_response
        _wayback_response="$(
          _download_from \
            "https://archive.org/wayback/available?url=${_target_url}"  \
            | grep --color=never 'closest'                              \
            | sed -E 's/.*"closest": {([^}]+)}.*/\1/g'                  \
            | sed -E 's/.*"url": "([^"]+)".*/\1/g' || printf ""
        )"
        if [[ -n "${_wayback_response:-}" ]]
        then
          _target_url="${_wayback_response}"
        else
          _exit_1 \
            printf "No archived version available from the Wayback Machine.\\n"
        fi
      fi
    fi

    # `bookmark peek`
    if [[ "${_subcommand}" == "peek" ]]
    then
      _bookmark_view_in_terminal_browser "${_target_url}"
      return 0
    # `bookmark open`
    elif [[ "${_subcommand}" == "open" ]]
    then
      if _command_exists "xdg-open"
      then
        xdg-open "${_target_url}"
        return 0
      elif _command_exists "open"
      then
        open "${_target_url}"
        return 0
      else
        _exit_1 printf \
          "%s doesn't know how to open URLs on your system.\\n" \
          "$(_color_primary "${_ME}")"
      fi
    # `bookmark url`
    elif [[ "${_subcommand}" == "url" ]]
    then
      printf "%s\\n" "${_target_url}"
      return 0
    fi
  fi

  # `bookmark`
  if [[ -z "${_url:-}" ]]
  then
    if [[ -z "${_arguments[*]:-}" ]]
    then
      local _header=
      _header="Add: $(_color_primary "${_ME} <url>")"
      _header="${_header} Help: $(_color_primary "${_ME} help bookmark")"

      cat <<HEREDOC
${_header}
$(_color_secondary "------------------------------------")
HEREDOC
    fi

    _bookmark list "${_arguments[@]:-}"
    return 0
  fi

  # `bookmark <url>`
  local _temp_file=
  _temp_file="$(mktemp -t nb-bookmark-tempfile.XXXXXX)"

  local _download_url="${_url}"
  _download_url="$(
    printf "%s\\n" "${_download_url}" \
      | sed 's/https:\/\/twitter.com/https:\/\/mobile.twitter.com/g'
  )"

  if ! _download_from "${_download_url}" "${_temp_file}" ||
     [[ ! -e "${_temp_file}"                 ]] ||
     [[ "$(wc -c <"${_temp_file}")" == 0     ]]
  then
    _warn \
      printf "Unable to download page at %s\\n" "$(_color_primary "${_url}")"
  fi

  if _file_is_text "${_temp_file}"
  then
    if [[ -z "${_title}" ]]
    then
      _title="$(
        cat "${_temp_file}"                               \
          | grep -o -E -i '.*<title[^>]*>[^<]*</title>'   \
          | head -1                                       \
          | sed 's/.*<title[^>]*>//'                      \
          | sed 's/.*<TITLE[^>]*>//'                      \
          | sed 's/<\/title>.*//'                         \
          | sed 's/<\/TITLE>.*//'
      )" || : # TODO: pipeline returns error status but still returns title
    fi

    if [[ -z "${_title}" ]]
    then
      _title="$(
        cat "${_temp_file}" \
          | sed -E -n       \
            's/.*<meta .*property=.og:title[^>]*content="([^"]+)".*/\1/p' \
            2>/dev/null || :
      )"
    fi
  fi

  local _domain=
  _domain="$(printf "%s\\n" "${_url}" | awk -F[/:] '{print $4}')"

  if [[ -n "${_domain:-}" ]]
  then
    _title+=" (${_domain})"
  fi

  if [[ -n "${_title:-}" ]]
  then
    if _command_exists "w3m"
    then # convert html entities TODO: _browser --convert-entities
      _title="$(
        printf "%s\\n" "${_title}" | w3m -dump -T text/html -cols 99999
      )"
    fi

    _bookmark_content="# ${_title}${_NEWLINE}${_NEWLINE}"
  fi

  _bookmark_content+="<${_url}>${_NEWLINE}"

  if _file_is_text "${_temp_file}"
  then
    local _description=
    _description="$(
      cat "${_temp_file}"   \
        | sed -E -n         \
            's/.*<meta .*name=.description.[^>]*content="([^"]+)".*/\1/p' \
            2>/dev/null || :
    )"

    if [[ -z "${_description:-}" ]]
    then
      _description="$(
        cat "${_temp_file}" \
          | sed -E -n       \
              's/.*<meta .*property=.og:description[^>]*content="([^"]+)".*/\1/p' \
              2>/dev/null || :
      )"
    fi

    if [[ -n "${_description}" ]]
    then
      if _command_exists "w3m"
      then # convert html entities TODO: _browser --convert-entities
        _description="$(
          printf "%s\\n" "${_description}" | w3m -dump -T text/html -cols 99999
        )"
      fi

      _bookmark_content+="${_NEWLINE}## Description${_NEWLINE}"
      _bookmark_content+="${_NEWLINE}${_description}${_NEWLINE}"
    fi
  fi

  if [[ -n "${_quote}" ]]
  then
    _bookmark_content+="${_NEWLINE}## Quote${_NEWLINE}"

    local _counter=0

    while IFS= read -r __line
    do
      if ! ((_counter))
      then
        _bookmark_content+="${_NEWLINE}> ${__line}${_NEWLINE}"
      elif [[ -z "${__line:-}" ]]
      then
        _bookmark_content+=">${_NEWLINE}"
      else
        _bookmark_content+="> ${__line}${_NEWLINE}"
      fi

      _counter="$((_counter+1))"
    done <<< "${_quote}"
  fi

  if [[ -n "${_comment}" ]]
  then
    _bookmark_content+="${_NEWLINE}## Comment${_NEWLINE}"
    _bookmark_content+="${_NEWLINE}${_comment}${_NEWLINE}"
  fi

  if [[ -n "${_related_urls[*]:-}" ]]
  then
    _bookmark_content+="${_NEWLINE}## Related${_NEWLINE}${_NEWLINE}"

    for __related_url in "${_related_urls[@]:-}"
    do
      _bookmark_content+="- <${__related_url}>${_NEWLINE}"
    done
  fi

  if [[ -n "${_tag_list}" ]]
  then
    local _tags=()
    local _tag_string=

    IFS=',' read -ra _tags <<< "${_tag_list}"
    for __tag in "${_tags[@]:-}"
    do
      local _normalized_tag
      _normalized_tag="$(printf "%s\\n" "${__tag}" | tr -d '#')"

      if [[ -z "${_tag_string}" ]]
      then
        _tag_string+="#${_normalized_tag}"
      else
        _tag_string+=" #${_normalized_tag}"
      fi
    done

    _bookmark_content+="${_NEWLINE}## Tags${_NEWLINE}"
    _bookmark_content+="${_NEWLINE}${_tag_string}${_NEWLINE}"
  fi

  if _file_is_text "${_temp_file}"
  then
    if ! ((_skip_content))
    then
      local _processed_content

      if _command_exists "pandoc" || _command_exists "readable"
      then
        _processed_content="$(
          cat "${_temp_file}" \
            | if _command_exists "readable"
              then
                readable --base "${_url}" 2>/dev/null || cat
              else
                cat
              fi |  if _command_exists "pandoc"
                    then
                      pandoc                                  \
                        --from html-native_divs-native_spans  \
                        --to markdown-grid_tables --wrap=none \
                        2>/dev/null || cat
                    else
                      cat
                    fi
        )"

        if [[ -n "${_processed_content}" ]]
        then
          _bookmark_content+="${_NEWLINE}## Content${_NEWLINE}"
          _bookmark_content+="${_NEWLINE}${_processed_content}${_NEWLINE}"
        fi
      else
        _save_source=1
      fi
    fi

    if ((_save_source))
    then
      _bookmark_content+="${_NEWLINE}## Source${_NEWLINE}"
      _bookmark_content+="${_NEWLINE}\`\`\`html${_NEWLINE}"
      _bookmark_content+="$(cat "${_temp_file}")"
      _bookmark_content+="${_NEWLINE}\`\`\`${_NEWLINE}"
    fi
  fi

  if [[ -n "${_bookmark_content}" ]]
  then
    local _basename=

    if [[ -z "${_target_filename:-}" ]]
    then
      _basename="$(
        _notebooks current --filename "$(date '+%Y%m%d%H%M%S').bookmark.md"
      )"
    else
      if [[ "${_target_filename}" =~ \. ]]
      then
        _basename="$(_notebooks current --filename "${_target_filename}")"
      else
        _basename="$(
          _notebooks current --filename "${_target_filename}.bookmark.md"
        )"
      fi
    fi

    local _add_options=("--filename" "${_basename}")

    ((_encrypt))              && _add_options+=("--encrypt")
    ((_edit_before_commit))   && _add_options+=("--edit")
    [[ -n "${_password:-}" ]] && _add_options+=("--password" "${_password}")

    printf "%s" "${_bookmark_content}" | _add "${_add_options[@]}"

    [[ -f "${_temp_file:-}" ]] && rm "${_temp_file:?}"
  fi
}
_alias_subcommand "bookmark" "b"
_alias_subcommand "bookmark" "bookmarks"

# completions ##################################################### completions

describe "completions" <<HEREDOC
Usage:
  ${_ME} completions (check | install [-d | --download] | uninstall)

Options:
  -d, --download  Download the completion scripts and install.

Description:
  Manage completion scripts. For more information, visit:
  https://github.com/${_REPO}/blob/${_REPO_MAIN_BRANCH}/etc/README.md
HEREDOC
_completions() {
  local _BASH_COMP_NAME="nb"
  local _ZSH_COMP_NAME="_nb"

  # Usage: _completions_check
  _completions_check() {
    local _bash_completion_path=
    _bash_completion_path="$(_get_bash_completion_path)"

    local _exists=0

    if [[ -n "${_bash_completion_path:-}" ]] &&
       [[ -d "${_bash_completion_path}"   ]]
    then
      if [[ -w "${_bash_completion_path}"   ]]
      then
        if [[ -e "${_bash_completion_path}/${_BASH_COMP_NAME}" ]]
        then
          _exists=1
          printf "Exists: %s\\n" "${_bash_completion_path}/${_BASH_COMP_NAME}"
        fi
      else
        _warn printf "Permission denied: %s\\n" "${_bash_completion_path}"
      fi
    fi

    local _zsh_completion_path="/usr/local/share/zsh/site-functions"

    if [[ -d "${_zsh_completion_path}" ]]
    then
      if [[ -w "${_zsh_completion_path}" ]]
      then
        if [[ -e "${_zsh_completion_path}/${_ZSH_COMP_NAME}" ]]
        then
          _exists=1
          printf "Exists: %s\\n" "${_zsh_completion_path}/${_ZSH_COMP_NAME}"
        fi
      else
        _warn printf "Permission denied: %s\\n" "${_zsh_completion_path}"
      fi
    fi

    if ! ((_exists))
    then
      _exit_1 printf "Completion scripts not found.\\n"
    fi
  }

  # Usage: _get_bash_completion_path
  _get_bash_completion_path() {
    local _bash_completion_path=

    if [[ -n "${BASH_COMPLETION_COMPAT_DIR:-}" ]]
    then
      _bash_completion_path="${BASH_COMPLETION_COMPAT_DIR}"
    fi

    if [[ -z "${_bash_completion_path:-}" ]]
    then
      local _maybe_path
      _maybe_path="$(
        pkg-config \
          --variable=completionsdir bash-completion 2>/dev/null || true
      )"

      if [[ -n "${_maybe_path:-}" ]]
      then
        _bash_completion_path="${_maybe_path}"
      fi
    fi

    if [[ -z "${_bash_completion_path:-}"       ]] &&
       [[ -d "/usr/local/etc/bash_completion.d" ]]
    then
      _bash_completion_path="/usr/local/etc/bash_completion.d"
    fi

    if [[ -z "${_bash_completion_path:-}" ]] &&
       [[ -d "/etc/bash_completion.d"     ]]
    then
      _bash_completion_path="/etc/bash_completion.d"
    fi

    printf "%s\\n" "${_bash_completion_path:-}"
  }

  # Usage: _completions_install [--download]
  _completions_install() {
    local _download=0
    if [[ "${1:-}" == "--download" ]]
    then
      _download=1
    fi

    local _my_path="${0}"

    if [[ -L "${_my_path}" ]]
    then
      if hash "realpath" 2>/dev/null
      then
        _my_path="$(realpath "${_my_path}")"
      else
        _my_path="$(readlink "${_my_path}")"
      fi
    fi

    local _my_dir=
    _my_dir="$(cd "$(dirname "${_my_path}")"; pwd)"
    if [[ -z "${_my_dir}" ]] || [[ ! -d "${_my_dir}" ]]
    then
      exit 1
    fi

    if [[ -z "${_REPO:-}" ]] || [[ -z "${_REPO_RAW_URL:-}" ]]
    then
      _exit_1 printf "Source Git repository not configured.\\n"
    fi

    for __shell in bash zsh
    do
      local _completion_source="${_my_dir}/etc/${_ME}-completion.${__shell}"

      if ((_download))
      then
        if [[ ! -f "${_completion_source}" ]]
        then
          _completion_source="$(mktemp)"
          local _completion_url="${_REPO_RAW_URL}/etc/${_ME}-completion.${__shell}"

          if ! _download_from "${_completion_url}" "${_completion_source}"
          then
            _exit_1 printf "Unable to download completion script from %s\\n" \
              "${_completion_source}"
          fi
        fi
      fi

      if [[ ! -f "${_completion_source}" ]]
      then
        cat <<HEREDOC
Unable to find source ${__shell} completion script. You can try downloading
and installing the latest version with the following command (\`sudo\` might
be necessary):
  ${_ME} completions install --download

More Information: ${__shell}
  https://github.com/${_REPO}/blob/${_REPO_MAIN_BRANCH}/etc/README.md
HEREDOC
      else
        local _completion_path=
        local _completion_target=
        if [[ "${__shell}" == "bash" ]]
        then
          _completion_path="$(_get_bash_completion_path)"
          _completion_target="${_completion_path}/${_BASH_COMP_NAME}"
        elif [[ "${__shell}" == "zsh" ]]
        then
          _completion_path="/usr/local/share/zsh/site-functions"
          _completion_target="${_completion_path}/${_ZSH_COMP_NAME}"
        fi

        if [[ -n "${_completion_path:-}" ]] &&
           [[ -d "${_completion_path}"   ]]
        then
          if [[ -w "${_completion_path}" ]]
          then
            if [[ ! -e "${_completion_target}" ]]
            then
              cp \
                "${_completion_source}" \
                "${_completion_target}"
              chmod +r "${_completion_target}"
              printf "Completion script installed: %s\\n" \
                "${_completion_target}"
            else
              _warn printf "Exists: %s\\n" "${_completion_target}"
            fi
          else
            _warn printf "Permission denied: %s\\n" "${_completion_path}"
          fi
        fi
      fi
    done
  }

  # Usage: _completions_uninstall
  _completions_uninstall() {
    local _completion_path=
    local _completion_target=

    for __shell in bash zsh
    do
      if [[ "${__shell}" == "bash" ]]
      then
        _completion_path="$(_get_bash_completion_path)"
        _completion_target="${_completion_path}/${_BASH_COMP_NAME}"
      elif [[ "${__shell}" == "zsh" ]]
      then
        _completion_path="/usr/local/share/zsh/site-functions"
        _completion_target="${_completion_path}/${_ZSH_COMP_NAME}"
      fi

      if [[ -n "${_completion_path:-}" ]] &&
         [[ -d "${_completion_path}"   ]]
      then
        if [[ -w "${_completion_path}"   ]] &&
           [[ -w "${_completion_target}" ]]
        then
          if [[ -f "${_completion_target}" ]]
          then
            rm "${_completion_target:?}"

            printf "Completion script removed: %s\\n" \
              "${_completion_target}"
          fi
        else
          _warn printf "Permission denied: %s\\n" "${_completion_path}"
        fi
      fi
    done
  }

  local _subcommand="${1:-}"

  case "${_subcommand}" in
    check)
      _completions_check
      ;;
    install)
      if [[ "${2:-}" =~ ^-d|--download$ ]]
      then
        _completions_install --download
      else
        _completions_install
      fi
      ;;
    uninstall)
      _completions_uninstall
      ;;
    *)
      _help "completions"
      return 0
      ;;
  esac
}

# count ################################################################# count

describe "count" <<HEREDOC
Usage:
  ${_ME} count

Description:
  Print the number of items in the current notebook.
HEREDOC
_count() {
  local _notebook_path
  _notebook_path="$(_notebooks current --path)"

  _list_files "${_notebook_path}" | wc -l | tr -d ' '
}

# delete ############################################################### delete

describe "delete" <<HEREDOC
Usage:
  ${_ME} delete (<id> | <filename> | <path> | <title>) [-f | --force]

Options:
  -f, --force   Skip the confirmation prompt.

Description:
  Delete a note.

Examples:
  ${_ME} delete 3
  ${_ME} delete example.md
  ${_ME} delete "A Document Title"
  ${_ME} 3 delete --force
  ${_ME} example:delete 12
  ${_ME} delete example:12
  ${_ME} example:12 delete
  ${_ME} d 3
  ${_ME} 3 d
  ${_ME} d example:12
  ${_ME} example:12 d

Shortcut Alias: \`d\`
HEREDOC
_delete() {
  local _force=0
  local _selector=
  local _title=

  for __arg in "${@:-}"
  do
    case "${__arg}" in
      -f|--force)
        _force=1
        ;;
      *)
        if [[ -z "${_selector}" ]]
        then
          _selector="${__arg}"
        fi
        ;;
    esac
  done

  if [[ -z "${_selector:-}" ]]
  then
    _exit_1 _help "delete"
  fi

  local _basename
  _basename="$(_show "${_selector}" --filename)"

  local _notebook_path
  _notebook_path="$(_notebooks current --path)"

  if [[ -z "${_basename}" ]]
  then
    _exit_1 printf "Not found: %s\\n" "$(_color_primary "${_selector}")"
  fi

  local _info
  _info="$(_show "${_basename}" --info-line)"

  if ! ((_force))
  then
    printf "Deleting: %s\\n" "${_info}"
    while true
    do
      IFS='' read -r -e -d $'\n' -p "\
$(_color_primary "Proceed?")  $(_color_brackets "y/N") " __yn

      case ${__yn} in
        [Yy]*)
          break
          ;;
        *)
          printf "Exiting...\\n"
          exit 0
          ;;
      esac
    done
  fi

  if [[ -n "${_basename}" ]] && [[ -e "${_notebook_path}/${_basename}" ]]
  then
    if [[ -d "${_notebook_path}/${_basename}" ]] &&
       [[ -z "$(ls -A "${_notebook_path}/${_basename}")" ]]
    then
      rm -r "${_notebook_path:?}/${_basename:?}"
    else
      git -C "${_notebook_path:?}" rm -r "${_basename:?}" 1>/dev/null
    fi

    if [[ ! -e "${_notebook_path}/${_basename}" ]]
    then
      _index delete "${_basename}"

      _git checkpoint "${_notebook_path}" "[nb] Delete: ${_basename}"

      printf "Deleted:  %s\\n" "${_info}"
    fi
  else
    _exit_1 printf "Selection not found.\\n"
  fi
}
_alias_subcommand "delete" "d"

# edit ################################################################### edit

describe "edit" <<HEREDOC
Usage:
  ${_ME} edit (<id> | <filename> | <path> | <title>)
          [-c <content> | --content <content>] [--edit]
          [-e <editor> | --editor <editor>]

Options:
  -c, --content <content>  The content for the new note.
  --edit                   Open the note in the editor before saving when
                           content is piped or passed as an argument.
  -e, --editor <editor>    Edit the note with <editor>, overriding the editor
                           specified in the \`\$EDITOR\` environment variable.

Description:
  Open the specified note in \`\$EDITOR\`, currently set to "${EDITOR}", or
  <editor> if specified. Content piped to \`${_ME} edit\` or passed using the
  \`--content\` option will will be appended to the file without opening it
  in the editor, unless the \`--edit\` flag is specified.

  Non-text files will be opened in your system's preferred app or program for
  that file type.

Examples:
  ${_ME} edit 3
  ${_ME} edit example.md
  ${_ME} edit "A Document Title"
  echo "Content to append." | ${_ME} edit 1
  ${_ME} 3 edit
  ${_ME} example:edit 12
  ${_ME} edit example:12
  ${_ME} example:12 edit
  ${_ME} e 12
  ${_ME} 12 e
  ${_ME} e example:12
  ${_ME} example:12 e

Shortcut Alias: \`e\`
HEREDOC
_edit() {
  local _content=
  local _edit_before_commit=0
  local _local_editor=
  local _password=

  while ((${#}))
  do
    local __arg="${1:-}"
    local __val="${2:-}"

    case "${__arg}" in
      -c|--content)
        _content="$(_option_get_value "${__arg}" "${__val:-}")"
        shift
        ;;
      --edit)
        _edit_before_commit=1
        ;;
      -e|--editor)
        _local_editor="$(_option_get_value "${__arg}" "${__val:-}")"
        shift
        ;;
      --password)
        _password="$(_option_get_value "${__arg}" "${__val:-}")"
        shift
        ;;
      *)
        if [[ -z "${_selector:-}" ]]
        then
          _selector="${__arg:-}"
        fi
        ;;
    esac

    shift
  done

  if [[ -z "${_selector:-}" ]]
  then
    _exit_1 _help "edit"
  fi

  local _basename
  _basename="$(_show "${_selector:-}" --filename)"

  local _notebook_path
  _notebook_path="$(_notebooks current --path)"

  if [[ -z "${_basename}" ]]
  then
    _exit_1 printf "Not found: %s\\n" "$(_color_primary "${_selector}")"
  fi

  if [[ -n "${_basename}" ]] || [[ -e "${_notebook_path}/${_basename}" ]]
  then # _basename is assigned and a note exists with that name.
    local _before
    _before="$(_get_hash "${_notebook_path}/${_basename}")"
    local _decrypted_path=
    local _encrypted_path=
    local _target_file="${_notebook_path}/${_basename}"

    if _file_is_encrypted "${_target_file}"
    then
      _encrypted_path="${_target_file}"

      if [[ -z "${_password}" ]]
      then
        # request password without displaying it
        printf "%s: " "$(_color_primary "Password")"
        read -r -s _password </dev/tty
        printf "\\n" # print newline to stop `read`
      fi

      _decrypted_path="$(_decrypt_file "${_target_file}" "${_password}")"

      _target_file="${_decrypted_path}"

      _before="$(_get_hash "${_decrypted_path}")"
    fi

    if [[ -n "${_content}" ]]
    then
      printf "%s\\n" "${_content}" >> "${_target_file}"
    fi

    if ! _interactive_input
    then # piped input
      cat >> "${_target_file}"
    fi

    if ((_edit_before_commit)) || {
       _interactive_input && [[ -z "${_content:-}" ]]
    }
    then
      if [[ -n "${_local_editor}" ]]
      then
        EDITOR="${_local_editor}" _edit_file "${_target_file}"
      else
        _edit_file "${_target_file}"
      fi
    fi

    _after="$(_get_hash "${_notebook_path}/${_basename}")"

    if [[ -n "${_encrypted_path:-}" ]] && [[ -e "${_decrypted_path:-}" ]]
    then
      _after="$(_get_hash "${_decrypted_path}")"

      if [[ "${_before}" != "${_after}" ]]
      then
        local _temp_encrypted_path=
        _temp_encrypted_path="$(_temp file "${_basename}").tmp"

        mv "${_encrypted_path}" "${_temp_encrypted_path}"
        _encrypt_file "${_decrypted_path}" "${_encrypted_path}" "${_password}"

        if [[ -e "${_encrypted_path}" ]]
        then
          rm "${_temp_encrypted_path:?}"
        else
          mv "${_temp_encrypted_path}" "${_encrypted_path}"
        fi
      fi

      rm "${_decrypted_path:?}"
    fi

    _git checkpoint "${_notebook_path}" "[nb] Edit: ${_basename}"

    if [[ "${_before}" != "${_after}" ]]
    then
      local _info
      _info="$(_show "${_basename}" --info-line)"

      printf "Updated: %s\\n" "${_info}"
    fi
  else
    _exit_1 printf "Selection not found.\\n"
  fi
}
_alias_subcommand "edit" "e"

# env ##################################################################### env

# TODO: vim highlighting bug \`"

describe "env" <<HEREDOC
Usage:
  ${_ME} env [install]

Subcommands:
  install  Install dependencies on supported systems.

Description:
  Print program environment and configuration information, or install
  dependencies.
HEREDOC
_env() {
  # Usage: _env_install_deb <name> <version> <repo>
  _env_install_deb() {
    local _deb_filename=
    local _name="${1:?}"
    local _repo="${3:?}"
    local _version="${2:?}"
    local _x86_64_bit=1

    local _arch
    _arch="$(uname -m)"

    case "${_arch:-}" in
      x86_64)
        _deb_filename="${_name}_${_version}_amd64.deb"
        ;;
      aarch64)
        _x86_64_bit=0
        _deb_filename="${_name}_${_version}_arm64.deb"
        ;;
      arm*)
        _x86_64_bit=0
        _deb_filename="${_name}_${_version}_armhf.deb"
        ;;
    esac

    if [[ -z "${_deb_filename:-}"   ]]  || {
         [[ "${_name}" == "ripgrep" ]]  && ! ((_x86_64_bit))
       }
    then
      _warn printf "\
Please install %s manually. No .deb package available for %s."  \
        "$(_color_primary "${_name}")"                          \
        "$(_color_primary "${_arch}")"
      return 0
    fi

    local _deb_path="${HOME}/${_deb_filename}"

    # handle bat version prefix in URL
    [[ "${_name}" == "bat" ]] && _version="v${_version}"

    wget                \
      -O "${_deb_path}" \
      "https://github.com/${_repo}/releases/download/${_version}/${_deb_filename}"

    if [[ -e "${_deb_path}" ]]
    then
      dpkg -i "${_deb_path}"
      rm "${_deb_path:?}"
    fi
  }

  # Usage: _env_max_version <version> <version>
  _env_max_version() {
    printf "%s\\n" "${@:?}"                           \
      | sort -r -t '.' -k 1,1 -k 2,2 -k 3,3 -k 4,4 -g \
      | head -n 1
  }

  local _subcommand="${1:-}"
  if [[ -n "${_subcommand:-}" ]] && [[ "${_subcommand}" == "install" ]]
  then
    # packages: curl pandoc pygments w3m ripgrep tig
    if [[ -x "$(command -v apk)" ]]
    then
      _completions install --download

      apk add --no-cache  \
        bash-completion   \
        curl              \
        pandoc            \
        py3-pygments      \
        ripgrep           \
        tig               \
        w3m
    elif [[ -x "$(command -v apt-get)" ]]
    then
      _completions install --download

      local _apt_packages=()
      local _deb_packages=()
      local _dependencies=(
        bash-completion
        curl
        pandoc
        python3-pygments
        tig
        w3m
        ripgrep
        bat
      )

      [[ "$(cat /etc/os-release)" =~ ubuntu|debian ]] || return 1

      apt-get update

      for __package in "${_dependencies[@]}"
      do
        if ! _command_exists "${__package}" &&
           apt-cache show "${__package}" | grep -q "Package: ^${__package}"
        then
          _apt_packages+=("${__package}")
        fi
      done

      apt-get install -y "${_apt_packages[@]:-}"

      # force install of bat
      apt-get install -y bat -o Dpkg::Options::="--force-overwrite"

      _command_exists "rg"      ||
        _deb_packages+=("ripgrep 12.1.1 BurntSushi/ripgrep")
      _command_exists "batcat"  ||
        _deb_packages+=("bat 0.16.0 sharkdp/bat")

      for __package in "${_deb_packages[@]}"
      do
        local _arguments
        IFS=' ' read -ra _arguments <<< "${__package:-}"

        _env_install_deb "${_arguments[@]:-}"
      done
    elif [[ -x "$(command -v dnf)" ]]
    then
      : # TODO: dnf install
      _completions install --download
    elif [[ -x "$(command -v zypper)" ]]
    then
      : # TODO: zypper install
      _completions install --download
    else
      _completions install --download
    fi
  elif [[ -n "${_subcommand:-}" ]] && [[ "${_subcommand}" == "uninstall" ]]
  then
    _completions uninstall
  else
    printf "_ME=%s\\n" "${_ME:-}"
    printf "_MY_PATH=%s\\n" "${_MY_PATH:-}"
    printf "NB_NOTEBOOK_PATH=%s\\n" "${NB_NOTEBOOK_PATH:-}"
    printf "BROWSER=%s\\n" "${BROWSER:-}"
    printf "EDITOR=%s\\n" "${EDITOR:-}"
    printf "PAGER=%s\\n" "${PAGER:-}"
    printf "NB_AUTO_SYNC=%s\\n" "${NB_AUTO_SYNC:-}"
    printf "NB_DIR=%s\\n" "${NB_DIR:-}"
    printf "NB_DEFAULT_EXTENSION=%s\\n" "${NB_DEFAULT_EXTENSION:-}"
    printf "NB_ENCRYPTION_TOOL=%s\\n" "${NB_ENCRYPTION_TOOL:-}"
    printf "NB_COLOR_PRIMARY=%s\\n" "${NB_COLOR_PRIMARY:-}"
    printf "NB_COLOR_SECONDARY=%s\\n" "${NB_COLOR_SECONDARY:-}"
    printf "NB_COLOR_THEME=%s\\n" "${NB_COLOR_THEME:-}"
    printf "NB_FOOTER=%s\\n" "${NB_FOOTER:-}"
    printf "NB_HEADER=%s\\n" "${NB_HEADER:-}"
    printf "NB_LIMIT=%s\\n"  "${NB_LIMIT:-}"
    printf "NB_SYNTAX_THEME=%s\\n"  "${NB_SYNTAX_THEME:-}"
    printf "NBRC_PATH=%s\\n" "${NBRC_PATH:-}"
    printf "ack:        %s\\n" "$(command -v ack)"
    printf "ag:         %s\\n" "$(command -v ag)"
    printf "bat:        %s\\n" "$(command -v bat)"
    printf "code:       %s\\n" "$(command -v code)"
    printf "curl:       %s\\n" "$(command -v curl)"
    printf "emacs:      %s\\n" "$(command -v emacs)"
    printf "git:        %s\\n" "$(command -v git)"
    printf "gvim:       %s\\n" "$(command -v gvim)"
    printf "gpg:        %s\\n" "$(command -v gpg)"
    printf "highlight:  %s\\n" "$(command -v highlight)"
    printf "less:       %s\\n" "$(command -v less)"
    printf "lynx:       %s\\n" "$(command -v lynx)"
    printf "macdown:    %s\\n" "$(command -v macdown)"
    printf "mate:       %s\\n" "$(command -v mate)"
    printf "mvim:       %s\\n" "$(command -v mvim)"
    printf "nano:       %s\\n" "$(command -v nano)"
    printf "nvim:       %s\\n" "$(command -v nvim)"
    printf "openssl:    %s\\n" "$(command -v openssl)"
    printf "pandoc:     %s\\n" "$(command -v pandoc)"
    printf "pygments:   %s\\n" "$(command -v pygmentize)"
    printf "readable:   %s\\n" "$(command -v readable)"
    printf "rg:         %s\\n" "$(command -v rg)"
    printf "subl:       %s\\n" "$(command -v subl)"
    printf "tig:        %s\\n" "$(command -v tig)"
    printf "w3m:        %s\\n" "$(command -v w3m)"
    printf "vim:        %s\\n" "$(command -v vim)"
    printf "bash --version: %s\\n" "$(bash --version | head -1)"
    printf "uname -a: %s\\n" "$(uname -a)"
  fi
}

# export ############################################################### export

describe "export" <<HEREDOC
Usage:
  ${_ME} export (<id> | <filename> | <path> | <title>) <path> [-f | --force]
            [<pandoc options>...]
  ${_ME} export notebook <name> [<path>]
  ${_ME} export pandoc (<id> | <filename> | <path> | <title>)
            [<pandoc options>...]

Options:
  -f, --force   Skip the confirmation prompt when overwriting an existing file.

Subcommands:
  (default)     Export a file to <path>. If <path> has a different extension
                than the source note, convert the note using \`pandoc\`.
  notebook      Export the notebook <name> to the current directory or <path>.
                Alias for \`${_ME} notebooks export\`.
  pandoc        Export the file to standard output or a file using \`pandoc\`.
                \`export pandoc\` prints to standard output by default.

Description:
  Export a file or notebook.

  If Pandoc [1] is available, convert the note from its current format
  to the format of the output file as indicated by the file extension
  in <path>. Any additional arguments are passed directly to Pandoc.
  See the Pandoc help information for available options.

    1. https://pandoc.org/

Examples:
  # Export an Emacs Org mode note
  ${_ME} export example.org /path/to/example.org

  # Export a Markdown note to HTML and print to standard output
  ${_ME} export pandoc example.md --from=markdown_strict --to=html

  # Export a Markdown note to a .docx Microsoft Office Word document
  ${_ME} export example.md /path/to/example.docx

  # Export note 12 in the "sample" notebook to HTML
  ${_ME} export sample:12 /path/to/example.html
HEREDOC
_export() {
  local _args=()
  local _force=0
  local _notebook_export=0
  local _pandoc_only=0
  local _selector=
  local _target_path=

  for __arg in "${@:-}"
  do
    case "${__arg}" in
      notebook*)
        _notebook_export=1
        ;;
      pandoc)
        _pandoc_only=1
        ;;
      -f|--force)
        _force=1
        ;;
      *)
        if [[ -z "${_selector:-}" ]]
        then
          _selector="${__arg}"
        elif ! ((_pandoc_only)) && [[ -z "${_target_path:-}" ]]
        then
          _target_path="${__arg}"
        else
          _args+=("${__arg}")
        fi
        ;;
    esac
  done

  if [[ -z "${_selector:-}" ]]
  then
    _exit_1 _help "export"
  fi

  if ((_notebook_export))
  then
    _notebooks export "${_selector:-}" "${_target_path:-}"
    return 0
  fi

  local _basename
  _basename="$(_show "${_selector}" --filename)"

  local _notebook_path
  _notebook_path="$(_notebooks current --path)"

  if [[ -z "${_basename:-}" ]] || [[ ! -e "${_notebook_path}/${_basename:-}" ]]
  then
    _exit_1 printf "Not found: %s\\n" "$(_color_primary "${_selector}")"
  fi

  if ((_pandoc_only))
  then
    if _command_exists 'pandoc'
    then
      if [[ -n "${_args[*]:-}" ]]
      then
        pandoc "${_notebook_path}/${_basename}" "${_args[@]:-}"
      else
        pandoc "${_notebook_path}/${_basename}"
      fi
    else
      _exit_1 printf "Pandoc required. https://pandoc.org/\\n"
    fi
  else
    if [[ -z "${_target_path:-}" ]]
    then
      _exit_1 _help "export"
    fi

    if [[ -d "${_target_path}" ]]
    then
      _target_path="$(printf "%s\\n" "${_target_path}" | sed -e 's/\/$//g')"
      _target_path="${_target_path}/${_basename}"
    fi

    if [[ -e "${_target_path:-}" ]]
    then
      if ! ((_force))
      then
        printf "File exists at %s\\n" "$(_color_primary "${_target_path}")"

        while true
        do
          IFS='' read -r -e -d $'\n' -p "\
$(_color_primary "Overwrite existing file?") $(_color_brackets "y/N") " __yn

          case ${__yn} in
            [Yy]*)
              break
              ;;
            *)
              printf "Exiting...\\n"
              exit 0
              ;;
          esac
        done
      fi
    fi

    _source_file_type="${_basename##*.}"
    _target_file_type="${_target_path##*.}"

    if _command_exists 'pandoc' &&
       [[ "${_source_file_type}" != "${_target_file_type}" ]]
    then
      if [[ -n "${_args[*]:-}" ]]
      then
        pandoc                              \
          -o "${_target_path}"              \
          --standalone                      \
          "${_notebook_path}/${_basename}"  \
          "${_args[@]:-}"
      else
        pandoc                  \
          -o "${_target_path}"  \
          --standalone          \
          "${_notebook_path}/${_basename}"
      fi
    else
      cp "${_notebook_path}/${_basename}" "${_target_path}"
    fi &&
      printf "Exported: %s\\n"  "$(_show "${_basename}" --info-line)"
      printf "To:        %s\\n" "$(_color_primary "${_target_path}")"
  fi
}

# git ##################################################################### git

describe "git" <<HEREDOC
Usage:
  ${_ME} git [checkpoint [<message>] | dirty]
  ${_ME} git <git-options>...

Subcommands:
  checkpoint    Create a new git commit in the current notebook and sync with
                the remote if \`${_ME} set auto_sync\` is enabled.
  dirty         0 (success, true) if there are uncommitted changes in
                <notebook-path>. 1 (error, false) if <notebook-path> is clean.

Description:
  Run \`git\` commands within the current notebook directory.

Examples:
  ${_ME} git status
  ${_ME} git diff
  ${_ME} git log
  ${_ME} example:git status
HEREDOC
_git() {
  # _git_autosyncable()
  #
  # Usage:
  #   _git_autosyncable [<notebook-path>]
  #
  # Exit / Error Status:
  #   0 (success, true)  If autosync should trigger.
  #   1 (error,  false)  If autosync should not trigger.
  _git_autosyncable() {
    ((_GIT_ENABLED)) || return 1

    local _current_timestamp=
    local _last_fetch_timestamp=
    local _maybe_fetch_timestamp=
    local _notebook_path="${1:-}"

    if [[ -z "${_notebook_path}" ]]
    then
      _notebook_path="$(_notebooks current --path)"
    fi

    if ! ((NB_AUTO_SYNC))
    then # autosync not enabled.
      return 1
    fi

    if ! git -C "${_notebook_path}" config --get remote.origin.url &>/dev/null
    then # there is no remote configured.
      return 1
    fi

    if [[ ! -e "${_notebook_path}/.git/FETCH_HEAD" ]]
    then # no previous fetches, but has an origin and autosync is enabled.
      return 0
    fi

    if _maybe_fetch_timestamp="$(
         stat -c %Y "${_notebook_path}/.git/FETCH_HEAD" 2>/dev/null
       )"
    then # GNU
      _last_fetch_timestamp="${_maybe_fetch_timestamp}"
    elif _maybe_fetch_timestamp="$(
           gstat -c %Y "${_notebook_path}/.git/FETCH_HEAD" 2>/dev/null
         )"
    then # GNU prefixed
      _last_fetch_timestamp="${_maybe_fetch_timestamp}"
    else
      _last_fetch_timestamp="$(
        stat -f '%m' "${_notebook_path}/.git/FETCH_HEAD"
      )"
    fi
    _current_timestamp="$(date +%s)"

    _diff=$((_current_timestamp-_last_fetch_timestamp))

    [[ "${_diff}" -gt 60 ]]
  }

  # _git_checkpoint()
  #
  # Usage:
  #   _git_checkpoint <notebook-path> <commit-message>
  #
  # Description:
  #   Commit all files in <notebook-path> with the provided <commit-message>.
  _git_checkpoint() {
    ((_GIT_ENABLED)) || return 0

    # Usage: _git_checkpoint_commit <notebook-path> <message>
    _git_checkpoint_commit() {
     local _notebook_path="${1:-}"
     local _message="${2:-}"

      if [[ -z "${_notebook_path:-}" ]] || [[ -z "${_message:-}" ]]
      then
        _exit_1 printf \
           "Usage: _git_checkpoint_commit <message> <notebook-path>"
      fi

      git -C "${_notebook_path}" add --all &&
        git -C "${_notebook_path}" commit -a -m "${_message}"
    }

    local _message=
    local _show_spinner=0
    local _notebook_path=

    for __arg in "${@:-}"
    do
      case "${__arg}" in
        --spinner)
          _show_spinner=1
          ;;
        *)
          if [[ -z "${_notebook_path}"  ]] &&
             [[ "${__arg}" =~ ^/        ]] &&
             [[ -d "${__arg}"           ]]
          then
            _notebook_path="${__arg}"
          elif [[ -z "${_message}" ]]
          then
            _message="${__arg}"
          fi
          ;;
      esac
    done

    if [[ -z "${_notebook_path:-}" ]]
    then
      _notebook_path="$(_notebooks current --path)"
    fi

    if [[ -z "${_message}" ]]
    then
      _message="[nb] Commit"
    fi

    if ((NB_AUTO_SYNC))
    then
      (
        {
          # only sync when the index is dirty
          if _git dirty "${_notebook_path}"
          then
            _git_checkpoint_commit "${_notebook_path}" "${_message}" &>/dev/null
          fi && GIT_TERMINAL_PROMPT=0 _git sync "${_notebook_path}"  &>/dev/null
        } || :
      ) &
    else
      (
        _git_checkpoint_commit "${_notebook_path}" "${_message}" &>/dev/null ||
          return 0
      ) &
    fi

    if ((_show_spinner))
    then
      _spinner  ${!}
    fi

    wait        ${!}
    return      ${?}
  }

  # _git_dirty()
  #
  # Usage:
  #   _git_dirty [<notebook-path>]
  #
  # Exit / Error Status:
  #   0 (success, true)  If there are uncommitted changes in <notebook-path>
  #                      or the current notebook.
  #   1 (error,  false)  If <notebook-path> or the current notebook is clean.
  _git_dirty() {
    ((_GIT_ENABLED)) || return 1

    local _target_path="${1:-}"

    if [[ -z "${_target_path:-}" ]]
    then
      _target_path="$(_notebooks current --path)"
    fi

    [[ -n "$(git -C "${_target_path}" status --porcelain)" ]]
  }

  # _git_out_of_sync()
  #
  # Usage:
  #   _git_out_of_sync [<notebook-path>]
  #
  # Exit / Error Status:
  #   0 (success, true)  If <notebook-path> or the current notebook is out of
  #                      sync with the remote.
  #   1 (error,  false)  If <notebook-path> or the current notebook is synced
  #                      with the remote.
  _git_out_of_sync() {
    ((_GIT_ENABLED)) || return 1

    local _notebook_path="${1:-}"

    if [[ -z "${_notebook_path:-}" ]]
    then
      _notebook_path="$(_notebooks current --path)"
    fi

    git -C "${_notebook_path}" status | grep -q 'diverged'
  }

  # _git_required()
  #
  # Usage:
  #   _git_required
  #
  # Description:
  #   Exit with `_exit_1` if `git` isn't found.
  _git_required() {
    ((_GIT_ENABLED)) || return 0

    if ! _command_exists "git"
    then
      _exit_1 cat <<HEREDOC
Welcome to $(_color_primary "${_ME}")!

Git is required, but wasn't found. Install Git, then run \`${_ME}\` again.

About Git: https://git-scm.com/
HEREDOC
    fi

    local _git_name=
    _git_name="$(git config user.name || printf '')"

    local _git_email=
    _git_email="$(git config user.email || printf '')"

    if [[ -z "${_git_name}"   ]] ||
       [[ -z "${_git_email}"  ]]
    then
      cat <<HEREDOC
Welcome to $(_color_primary "${_ME}")!

Git requires some additional setup before using $(_color_primary "${_ME}").
Enter the name and email address you'd like to use with Git.
Edits you make will be attributed to this name and email address.
HEREDOC
      if [[ -z "${_git_name}" ]]
      then
        while true
        do
          IFS='' read -r -e -d $'\n' -p "$(_color_primary "Name"): " __name

          if [[ -n "${__name}" ]]
          then
            git config --global user.name "${__name}"
            break
          fi
        done
      fi

      if [[ -z "${_git_email}" ]]
      then
        while true
        do
          IFS='' read -r -e -d $'\n' -p "$(_color_primary "Email"): " __email

          if [[ -n "${__email}" ]]
          then
            git config --global user.email "${__email}"
            break
          fi
        done
      fi
    fi
  }

  # _git_sync()
  #
  # Usage:
  #   _git_sync [<notebook-path>]
  #
  # Description:
  #   Sync <notebook-path> or the current notebook with the remote.
  _git_sync() {
    local _notebook_path="${1:-}"
    local _prompt="${GIT_TERMINAL_PROMPT:-1}"

    if [[ -z "${_notebook_path:-}" ]]
    then
      _notebook_path="$(_notebooks current --path)"
    fi

    if ! git -C "${_notebook_path}" remote get-url origin &>/dev/null
    then
      return 0
    fi

    local _git_branch=
    _git_branch="$(git -C "${_notebook_path}" rev-parse --abbrev-ref HEAD)"

    if ! GIT_TERMINAL_PROMPT=0 git -C "${_notebook_path}" fetch &>/dev/null
    then
      if ((_prompt))
      then # fetch again, displaying git prompt and/or errors after newline
        printf "\\n"

        if ! GIT_TERMINAL_PROMPT=1 git -C "${_notebook_path}" fetch
        then
          exit 1
        fi
      else
        return 1
      fi
    fi

    if ! git -C "${_notebook_path}" show-branch \
           "remotes/origin/${_git_branch}" > /dev/null 2>&1
    then
      _warn printf \
        "Remote branch not found: %s\\n" "$(_color_primary "${_git_branch}")"
      return 1
    fi

    if ! git -C "${_notebook_path}" rebase "origin/${_git_branch}" &>/dev/null
    then
      local _merge_error=1

      local _conflicted_files
      _conflicted_files=($(
        GIT_PAGER='' git -C "${_notebook_path}" diff --name-only --diff-filter=U
      ))

      local _conflicted_text_files=()
      local _conflicted_binary_files=()

      if [[ -n "${_conflicted_files[*]:-}" ]]
      then
        if _contains ".index" "${_conflicted_files[@]}"
        then
          # Remove git conflict markers to keep both.
          # More info: https://stackoverflow.com/a/55187779
          _sed_i              \
            -e '/^<<<<<<</d'  \
            -e '/^>>>>>>>/d'  \
            -e '/=======/d'   \
            "${_notebook_path}/.index"

          git -C "${_notebook_path}" add ".index"
        fi

        for __file in "${_conflicted_files[@]}"
        do
          [[ "${__file}" == ".index" ]] && continue

          if _file_is_text "${__file}"
          then
            _conflicted_text_files+=("${__file}")

            git -C "${_notebook_path}" add            \
              "${__file}"
          else
            git -C "${_notebook_path}" checkout       \
              "origin/${_git_branch}" -- "${__file}"

            local _conflicted_file=
            _conflicted_file="$(
              _notebooks show "${_notebook_path}"     \
                --filename  "${__file%%.*}--conflicted-copy.${__file#*.}"
            )"

            _conflicted_binary_files+=("${_conflicted_file}")

            git -C "${_notebook_path}" mv       \
              "${__file}" "${_conflicted_file}"

            git -C "${_notebook_path}" checkout \
              "${_git_branch}" -- "${__file}"

            git -C "${_notebook_path}" add "${_conflicted_file}"
            git -C "${_notebook_path}" add "${__file}"
          fi
        done

        GIT_EDITOR=true git -C "${_notebook_path}" rebase \
          --continue &>/dev/null &&
            _merge_error=0
      fi

      if ((_merge_error))
      then
        git -C "${_notebook_path}" rebase --abort

        _warn printf \
          "Merge conflict. Use \`%s git\` to merge manually.\\n" \
          "${_ME}"
        return 1
      else
        if ((${#_conflicted_text_files[@]}))
        then
          printf "\\n"
          _warn cat <<HEREDOC
Files containing conflicts:

$(for __file in "${_conflicted_text_files[@]}"
do
  printf "  %s:%s\\n" "${_notebook_path}" "${__file}"
done)

Resolve conflicts with \`${_ME} edit\`. More info: \`${_ME} help sync\`
HEREDOC
        fi

        if ((${#_conflicted_binary_files[@]}))
        then
          printf "\\n"
          _warn cat <<HEREDOC
Conflicted copies of binary files:

$(for __file in "${_conflicted_binary_files[@]}"
do
  printf "  %s:%s\\n" "${_notebook_path}" "${__file}"
done)
HEREDOC
        fi
      fi
    fi

    GIT_TERMINAL_PROMPT="${GIT_TERMINAL_PROMPT:-1}" \
      git -C "${_notebook_path}" push               \
      --set-upstream                                \
      origin "${_git_branch}"                       \
      &>/dev/null || {
        _warn printf "Unable to push to remote.\\n"
        return 1
      }
  }

  case "${1:-}" in
    *autosync*)
      _git_autosyncable "${2:-}"
      ;;
    checkpoint)
      shift
      _git_checkpoint "${@:-}"
      ;;
    dirty)
      _git_dirty "${2:-}"
      ;;
    out*of*sync|o|oos|out)
      _git_out_of_sync "${2:-}"
      ;;
    required|setup)
      _git_required
      ;;
    sync)
      _git_sync "${2:-}"
      ;;
    *)
      local _notebook_path
      _notebook_path="$(_notebooks current --path)"

      if [[ -n "${1:-}" ]]
      then
        git -C "${_notebook_path}" "${@}"
      else
        git -C "${_notebook_path}"
      fi
      ;;
  esac
}

# helpers ############################################################# helpers

describe "helpers" <<HEREDOC
Usage:
  ${_ME} helpers <name> [<options≥...]

Description:
  Call helper functions.
HEREDOC
_helpers() {
  local _name="${1:-}"
  local _options=("${@:2}")

  case "${_name:-}" in
    browser*)
      _browser                        "${_options[@]:-}"
      ;;
    decrypt*)
      _decrypt_file                   "${_options[@]:-}"
      ;;
    download*)
      _download_from                  "${_options[@]:-}"
      ;;
    edit*)
      _edit_file                      "${_options[@]:-}"
      ;;
    encrypt*)
      _encrypt_file                   "${_options[@]:-}"
      ;;
    *archive*)
      _file_is_archive                "${_options[@]:-}"
      ;;
    *audio*)
      _file_is_audio                  "${_options[@]:-}"
      ;;
    *bookmark*)
      _file_is_bookmark               "${_options[@]:-}"
      ;;
    *document*)
      _file_is_document               "${_options[@]:-}"
      ;;
    *encrypted*)
      _file_is_encrypted              "${_options[@]:-}"
      ;;
    *image*)
      _file_is_image                  "${_options[@]:-}"
      ;;
    *text*)
      _file_is_text                   "${_options[@]:-}"
      ;;
    *video*)
      _file_is_video                  "${_options[@]:-}"
      ;;
    *first*)
      _get_first_line                 "${_options[@]:-}"
      ;;
    *hash*)
      _get_hash                       "${_options[@]:-}"
      ;;
    *http*)
      _get_http_status                "${_options[@]:-}"
      ;;
    *title*)
      _get_title                      "${_options[@]:-}"
      ;;
    *unique*basename*)
      _get_unique_basename            "${_options[@]:-}"
      ;;
    *unique*path*)
      _get_unique_path                "${_options[@]:-}"
      ;;
    *highlight*|*syntax*)
      _highlight_syntax_if_available  "${_options[@]:-}"
      ;;
    *interactive*|*input*)
      _interactive_input              "${_options[@]:-}"
      ;;
    join)
      _join                           "${_options[@]:-}"
      ;;
    less*|*prompt)
      _less_prompt                    "${_options[@]:-}"
      ;;
    line*wrap*off|unwrap)
      _wrap off                       "${_options[@]:-}"
      ;;
    line*wrap*on|wrap)
      _wrap on                        "${_options[@]:-}"
      ;;
    list*files)
      _list_files                     "${_options[@]:-}"
      ;;
    *line)
      _print_line                     "${_options[@]:-}"
      ;;
    *padding)
      _print_padding                  "${_options[@]:-}"
      ;;
    print*welcome|welcome)
      _print_welcome                  "${_options[@]:-}"
      ;;
    spinner)
      _spinner                        "${_options[@]:-}"
      ;;
    *email)
      _string_is_email                "${_options[@]:-}"
      ;;
    *url)
      _string_is_url                  "${_options[@]:-}"
      ;;
    *)
      _exit_1 _help "helpers"
      ;;
  esac
}

# history ############################################################# history

describe "history" <<HEREDOC
Usage:
  ${_ME} history [<id> | <filename> | <path> | <title>]

Description:
  Display notebook history using \`tig\` [1] (if available) or \`git log\`.
  When a note is specified, the history for that note is displayed.

    1. https://github.com/jonas/tig

Examples:
  ${_ME} history
  ${_ME} history example.md
  ${_ME} 3 history
  ${_ME} example:history
  ${_ME} history example:
  ${_ME} example:history 12
  ${_ME} history example:12
  ${_ME} example:12 history
HEREDOC
_history() {
  local _basename=
  local _notebook_path=
  local _selector="${1:-}"

  if [[ -n "${_selector:-}" ]]
  then
    if [[ "${_selector}" =~ :$ ]] ||
       ! _basename="$(_show "${_selector}" --filename 2>/dev/null)"
    then
      if ! _notebook_path="$(_notebooks show "${_selector}" --path 2>/dev/null)"
      then
        _exit_1 printf "Not found: %s\\n" "$(_color_primary "${_selector:-}")"
      fi
    fi
  fi

  if [[ -z "${_notebook_path:-}" ]]
  then
    _notebook_path="$(_notebooks current --path)"
  fi

  cd "${_notebook_path}" || _exit_1 printf "_history() \`cd\` failed.\\n"

  local _log_command="git log"

  if _command_exists "tig"
  then
    _log_command="tig --all"
  fi

  if [[ -n "${_basename:-}" ]] && [[ -e "${_notebook_path}/${_basename}" ]]
  then
    eval "${_log_command} -- \"${_basename}\""
  else
    eval "${_log_command}"
  fi
}

# import ############################################################### import

describe "import" <<HEREDOC
Usage:
  ${_ME} import (<path>... | <url>)
  ${_ME} import copy <path>...
  ${_ME} import download <url> [--convert]
  ${_ME} import move <path>...
  ${_ME} import notebook <path> [<name>]

Options:
  --convert  Convert HTML content to Markdown.

Subcommands:
  (default) Copy or download the file(s) at <path> or <url>.
  copy      Copy the file(s) at <path> into the current notebook.
  download  Download the file at <url> into the current notebook.
  move      Move the file(s) at <path> into the current notebook.
  notebook  Import the local notebook at <path> to make it global.

Description:
  Copy, move, or download files into the current notebook or import
  a local notebook to make it global.

Examples:
  ${_ME} import ~/Pictures/example.png
  ${_ME} import ~/Documents/example.docx
  ${_ME} import https://example.com/example.pdf
  ${_ME} example:import https://example.com/example.jpg
  ${_ME} import ./*
  ${_ME} import ./*.md
HEREDOC
_import() {
  # _import_validate_path()
  #
  # Usage:
  #   _import_validate_path <path>
  _import_validate_path() {
    if [[ -z "${1:-}" ]]
    then
      _exit_1 _help "import"
    elif [[ ! -e "${1:-}" ]]
    then
      _exit_1 printf "File not found: %s\\n" "$(_color_primary "${1:-}")"
    fi
  }

  # _import_get_valid_basename()
  #
  # Usage:
  #   _import_get_valid_basename <basename>
  _import_get_valid_basename() {
    local _target_basename="${1:-}"

    local _current_notebook_path
    _current_notebook_path="$(_notebooks current --path)"

    if [[ -e "${_current_notebook_path}/${_target_basename}" ]]
    then
      _target_basename="$(_notebooks current --filename "${_target_basename}")"
    fi
    printf "%s\\n" "${_target_basename}"
  }

  local _basename=
  local _convert=0
  local _target_notebook_name=
  local _paths=()
  local _subcommand=
  local _url=

  for __arg in "${@:-}"
  do
    case "${__arg}" in
      copy|download|move)
        _subcommand="${__arg}"
        ;;
      notebook*)
        _subcommand="notebook"
        ;;
      --convert)
        _convert=1
        ;;
      *)
        if [[ -n "${_subcommand:-}" ]]
        then
          if [[ "${_subcommand}" == "download" ]]
          then
            _url="${__arg:-}"
          elif [[ "${_subcommand}" == "notebook"  ]] &&
               [[ -n "${_paths[*]:-}"             ]]
          then
            _target_notebook_name="${__arg:-}"
          else
            _paths+=("${__arg:-}")
          fi
        else
          if [[ "${__arg:-}" =~ ^http|^file\: ]]
          then
            _subcommand="download"
            _url="${__arg:-}"
          else
            _subcommand="copy"
            _paths+=("${__arg:-}")
          fi
        fi
        ;;
    esac
  done

  local _notebook_path
  _notebook_path="$(_notebooks current --path)"

  case "${_subcommand}" in
    copy)
      for __path in "${_paths[@]:-}"
      do
        _import_validate_path "${__path:-}"

        _basename="$(basename -- "${__path}")"
        _basename="$(_import_get_valid_basename "${_basename}")"

        if [[ -d "${__path}" ]]
        then
          cp -R "${__path}" "${_notebook_path}/${_basename}"
        else
          cp "${__path}" "${_notebook_path}/${_basename}"
        fi &&
          _index add "${_basename}" &&
            _git checkpoint "${_notebook_path}" "[nb] Import: ${_basename}"

        printf "Imported %s from %s\\n"         \
          "$(_show "${_basename}" --info-line)" \
          "$(_color_primary "${__path}")"
      done
      ;;
    download)
      _basename="$(basename -- "${_url}" | tr -d '[:space:]')"

      if [[ "${_url}" =~ \/$ ]]
      then
        _basename="${_basename}.html"
      fi
      _basename="$(_import_get_valid_basename "${_basename}")"

      if ((_convert))
      then
        if ! _download_from "${_url}"             \
            | pandoc                              \
              --from html                         \
              --to markdown                       \
              -o "${_notebook_path}/${_basename}"
        then
          _exit_1 printf "Unable to download and convert: %s\\n" "${_url}"
        fi
      else
        if ! _download_from "${_url}" "${_notebook_path}/${_basename}"
        then
          _exit_1 printf "Unable to download: %s\\n" "${_url}"
        fi
      fi &&
        _index add "${_basename}" &&
          _git checkpoint "${_notebook_path}" "\
[nb] Import: ${_basename}

Source:
${_url}
"
      printf "Imported %s from %s\\n"         \
        "$(_show "${_basename}" --info-line)" \
        "$(_color_primary "${_url}")"
      ;;
    move)
      for __path in "${_paths[@]:-}"
      do
        _import_validate_path "${__path:-}"
        _basename="$(basename -- "${__path}")"
        _basename="$(_import_get_valid_basename "${_basename}")"

        mv "${__path}" "${_notebook_path}/${_basename}" &&
          _index add "${_basename}"                     &&
            _git checkpoint "${_notebook_path}" "[nb] Import: ${_basename}"

        printf "Imported %s from %s\\n"         \
          "$(_show "${_basename}" --info-line)" \
          "$(_color_primary "${__path}")"
      done
      ;;
    notebook)
      _notebooks import "${_paths[0]:-}" "${_target_notebook_name:-}"
      return 0
      ;;
    *)
      _exit_1 _help "import"
      ;;
  esac
}

# index ################################################################# index

describe "index" <<HEREDOC
Usage:
  ${_ME} index add <filename>
  ${_ME} index delete <filename>
  ${_ME} index get_basename <id>
  ${_ME} index get_id <filename>
  ${_ME} index get_max_id
  ${_ME} index rebuild
  ${_ME} index reconcile
  ${_ME} index show
  ${_ME} index update <existing-filename> <new-filename>
  ${_ME} index verify
  ${_ME} index <subcommand> <options>... [<notebook-path>]

Subcommands:
  add           Add <filename> to the index.
  delete        Delete <filename> from the index.
  get_basename  Print the filename / basename at the specified <id>.
  get_id        Get the id for <filename>.
  get_max_id    Get the maximum id for the notebook.
  rebuild       Rebuild the index, listing files by last modified, reversed.
                Some ids will change. Prefer \`${_ME} index reconcile\`.
  reconcile     Remove duplicates and update index for added and deleted files.
  show          Print the index.
  update        Overwrite the <existing-filename> entry with <new-filename>.
  verify        Verify that the index matches the notebook contents.

Description:
  Manage the index for the current notebook. This subcommand is used
  internally by \`${_ME}\` and using it manually will probably corrupt
  the index. If something goes wrong with the index, fix it with
  \`${_ME} index reconcile\`. All subcommands take an optional
  <notebook-path> that can be used to circumvent the notebook lookup.

  The index is a text file named '.index' in the notebook directory. .index
  contains a list of filenames and the line number of each filename
  represents the id. .index is included in the git repository so ids are
  preserved across systems.
HEREDOC
_index() {
  local _subcommand="${1:-}"
  local _notebook_path="${2:-}"

  # Use `$NB_NOTEBOOK_PATH` directly for performance.
  local _notebook_path="${NB_NOTEBOOK_PATH}"

  local _index_path="${_notebook_path}/.index"

  if [[ ! -e "${_index_path}"           ]] &&
     [[ ! "${_subcommand}" == "rebuild" ]]
  then
    _index rebuild
  fi

  case "${_subcommand}" in
    add)
      local _basename="${2:-}"

      if [[ -z "${_basename}" ]]
      then
        _exit_1 _help index
      fi

      if [[ ! -e "${_notebook_path}/${_basename}" ]]
      then
        _exit_1 printf "File not found: %s\\n" "${_basename}"
      fi

      if ! grep -q "^${_basename}$" "${_index_path}"
      then
        printf "%s\\n" "${_basename}" >> "${_index_path}"
      fi
      ;;
    delete)
      local _basename="${2:-}"

      if [[ -z "${_basename}" ]]
      then
        _exit_1 _help index
      fi

      if grep -q "^${_basename}$" "${_index_path}"
      then
        _sed_i -e "s/^${_basename}$//g" "${_index_path}"
      else
        return 1
      fi
      ;;
    get_basename)
      # Usage: _index_get_basename <id> <index path>
      _index_get_basename() {
        if [[ -z "${1:-}" ]] || [[ "${1:-}" == "0" ]]
        then
          return 1
        fi

        sed "${1:-}q;d" "${2:-}"
      }

      local _id_number="${2:-}"
      if [[ -z "${_id_number}" ]]
      then
        _exit_1 _help index
      fi

      local _basename
      _basename="$(_index_get_basename "${_id_number}" "${_index_path}")"

      if [[ -z "${_basename}" ]]
      then
        _index reconcile &&
          _basename="$(_index_get_basename "${_id_number}" "${_index_path}")"
      fi

      if [[ -z "${_basename}" ]]
      then
        return 1
      else
        printf "%s\\n" "${_basename}"
      fi
      ;;
    get_id)
      # Usage: _index_get_id <basename> <index path>
      _index_get_id() {
        # Alternatives:
        # grep -n "^${1:-}$" "${2:-}" | cut -f1 -d:
        # awk 'match($0,v){print NR; exit}' v="^${1:-}$" "${2:-}"
        # rg --color=never --line-number "^${1:-}$" "${2:-}" | cut -d: -f1
        sed -n "/^${1:-}$/=" "${2:-}"
      }

      local _basename="${2:-}"
      if [[ -z "${_basename}" ]]
      then
        _help index
        return 1
      fi

      local _id
      _id="$(_index_get_id "${_basename}" "${_index_path}")"

      if [[ -z "${_id}" ]]
      then
        _index reconcile &&
          _id="$(_index_get_id "${_basename}" "${_index_path}")"
      fi

      if [[ -z "${_id}" ]]
      then
        return 1
      else
        printf "%s\\n" "${_id}"
      fi

      # Alternative
      # -----------

      # local _basename="${2:-}"
      # local _id

      # if [[ -z "${_basename}" ]]
      # then
      #   _help index
      #   return 1
      # fi

      # _index_get_id "${_basename:-}" "${_index_path:-}" | {
      #   read -r _id

      #   if [[ -n "${_id:-}" ]]
      #   then
      #     printf "%s\\n" "${_id:-}"
      #   else
      #     _index reconcile &&
      #       _index_get_id "${_basename:-}" "${_index_path:-}" | {
      #           read -r _id

      #           if [[ -n "${_id:-}" ]]
      #           then
      #             printf "%s\\n" "${_id:-}"
      #           else
      #             return 1
      #           fi
      #         }
      #   fi
      # }
      ;;
    get_max_id)
      wc -l < "${_index_path}" | tr -d '[:space:]'
      printf "\\n"
      ;;
    rebuild)
      ls -t -r "${_notebook_path}" > "${_index_path}" &&
        _git checkpoint "${_notebook_path}" "[nb] Rebuild Index"
      ;;
    reconcile)
      _index_reconcile_remove_duplicates() {
        local _reconcile_index_path="${1:-}"
        [[ -z "${_reconcile_index_path:-}" ]] && return 1

        local _temp_file
        _temp_file="$(mktemp)"

        exec 3> "${_temp_file}"
        exec 4< "${_temp_file}"

        rm "${_temp_file:?}"

        # Remove duplicates and preserve newlines.
        awk '!NF || !seen[$0]++' "${_reconcile_index_path}" >&3
        cat <&4 > "${_reconcile_index_path}"

        exec 3>&-
        exec 4<&-
      }

      local -a _index_list=()
      _index_list=($(<"${_index_path}"))

      local -a _file_list=()
      _file_list=($(ls "${_notebook_path}"))

      _index_reconcile_remove_duplicates "${_index_path}"

      local -a _index_file_list_diff=()
      _index_file_list_diff=($(
        printf "%s\\n"          \
          "${_index_list[@]:-}" \
          "${_file_list[@]:-}"  \
          | sort                \
          | uniq -u
      ))

      local -a _diff_in_index=()
      _diff_in_index=($(
        printf "%s\\n"                    \
          "${_index_list[@]:-}"           \
          "${_index_file_list_diff[@]:-}" \
          | sort                          \
          | uniq -d                       \
          | uniq
      ))

      for __item in "${_diff_in_index[@]:-}"
      do
        if [[ -n "${__item}" ]]
        then
          _contains "${__item}" "${_file_list[@]:-}" ||
            _index delete "${__item}"
        fi
      done

      local -a _diff_in_file_list=()
      _diff_in_file_list=($(
        printf "%s\\n"                    \
          "${_file_list[@]:-}"            \
          "${_index_file_list_diff[@]:-}" \
          | sort                          \
          | uniq -d                       \
          | uniq
      ))

      for __item in "${_diff_in_file_list[@]:-}"
      do
        if [[ -n "${__item}" ]]
        then
          _contains "${__item}" "${_index_list[@]:-}" ||
            _index add "${__item}"
        fi
      done
      ;;
    show)
      cat "${_index_path}"
      ;;
    update)
      local _old_basename="${2:-}"
      local _new_basename="${3:-}"

      if [[ -z "${_old_basename}" ]] || [[ -z "${_new_basename}" ]]
      then
        _exit_1 _help index
      fi

      if grep -q "^${_old_basename}$" "${_index_path}"
      then
        _sed_i -e "s/^${_old_basename}$/${_new_basename}/g" "${_index_path}"
      else
        return 1
      fi
      ;;
    verify)
      local _valid=1

      local -a _index_list=()
      _index_list=($(<"${_index_path}"))

      local -a _file_list=()
      _file_list=($(ls "${_notebook_path}"))

      local -a _index_file_list_diff=()
      _index_file_list_diff=($(
        printf "%s\\n"          \
          "${_index_list[@]:-}" \
          "${_file_list[@]:-}"  \
          | sort                \
          | uniq -u
      ))

      if [[ -n "${_index_file_list_diff[*]:-}" ]]
      then
        _valid=0
      fi

      for __item in $(printf "%s\\n" "${_index_list[@]:-}" | uniq -c)
      do
        local _count
        _count="$(printf "%s\\n" "${__item}" | awk '{print $1}')"

        if [[ "${_count}" -gt 1 ]]
        then
          _valid=0
          break
        fi
      done

      if ! ((_valid))
      then
        _exit_1 \
          printf "Index corrupted. To fix, run:\\n  %s index reconcile\\n" \
          "${_ME}"
      fi
      ;;
    *)
      :
      ;;
  esac
}

# init ################################################################### init

describe "init" <<HEREDOC
Usage:
  ${_ME} init [<remote-url>]

Description:
  Initialize the local data directory and generate a configuration file for \`${_ME}\`
  if it doesn't exist yet at:

      ${NBRC_PATH}

Examples:
  ${_ME} init
  ${_ME} init https://github.com/example/example.git
HEREDOC
_init() {
  # Usage: _init_create_home_notebook [<url>]
  _init_create_home_notebook() {
    local _repo_url="${1:-}"

    if [[ -n "${_repo_url}" ]]
    then
      git clone "${_repo_url}" "${NB_DIR}/home"
    else
      mkdir -p "${NB_DIR}/home"                   &&
        git -C "${NB_DIR}/home" init 1>/dev/null  &&
        touch "${NB_DIR}/home/.index"             &&
        _git checkpoint "${NB_DIR}/home" "[nb] Initialize"
    fi

    _notebooks use "home" 1>/dev/null

    printf "Created notebook:           %s\\n" "$(_color_primary "home")"
  }

  # Usage: _init_create_nb_dir
  _init_create_nb_dir() {
    if [[ -d "${NB_DIR}" ]]
    then
      printf "Data directory exists:      %s\\n" "$(_color_primary "${NB_DIR}")"
    elif [[ -e "${NB_DIR}" ]]
    then
      _exit_1 cat <<HEREDOC
Unable to set up the $(_color_primary "${_ME}") data directory. File exists at:
${NB_DIR}
HEREDOC
    else
      mkdir -p "${NB_DIR}"
      printf "Created data directory:     %s\\n" "$(_color_primary "${NB_DIR}")"
    fi
  }

  # Usage: _init_create_rc_file
  _init_create_rc_file() {
    if [[ -e "${NBRC_PATH}" ]]
    then
      printf "Configuration file exists:  %s\\n" \
        "$(_color_primary "${NBRC_PATH}")"
    else
      cat <<HEREDOC > "${NBRC_PATH}"
#!/usr/bin/env bash
###############################################################################
# .nbrc
#
# Configuration file for \`${_ME}\`, a command line note-taking, bookmarking,
# and knowledge base application with encryption, search, Git-backed syncing,
# and more in a single portable script.
#
# Edit this file manually or manage settings using the \`${_ME} settings\`
# subcommand. Configuration options are set as environment variables, eg:
#   export NB_ENCRYPTION_TOOL=gpg
#
# https://github.com/${_REPO}
###############################################################################
HEREDOC
      printf "Created configuration file: %s\\n" "$(_color_primary "${NBRC_PATH}")"
    fi
  }

  # Usage: _init_print_header
  _init_print_header() {
    printf "%s\\n" "$(_color_primary "Initializing...")"
  }

  if [[ -e "${NB_DIR}" ]] && [[ ! -d "${NB_DIR}" ]]
  then
    _exit_1 printf "NB_DIR exists and is not a directory: %s\\n" \
      "$(_color_primary "${NB_DIR}")"
  fi

  if [[ -d "${_GLOBAL_NOTEBOOK_PATH}" ]]
  then
    _exit_1 cat <<HEREDOC
$(_color_primary "${_ME}") already initialized. To initialize a local notebook, use:
  $(_color_primary "${_ME} notebooks init")
More Information:
  $(_color_primary "${_ME} help notebooks")
HEREDOC
  fi

  _print_welcome                                    &&
    _init_print_header                  1>/dev/null &&
    _init_create_nb_dir                 1>/dev/null &&
    _init_create_rc_file                1>/dev/null &&
    _init_create_home_notebook "${1:-}" 1>/dev/null

}

# list ################################################################### list

describe "list" <<HEREDOC
Usage:
  ${_ME} list [-e [<length>] | --excerpt [<length>]] [--filenames]
          [-n <limit> | --limit <limit> |  --<limit>] [--no-id]
          [--no-indicator] [-p | --pager] [--paths] [-s | --sort]
          [-r | --reverse] [-t <type> | --type <type> | --<type>]
          [<id> | <filename> | <path> | <title> | <query>]

Options:
  -e, --excerpt [<length>]        Print an excerpt <length> lines long under
                                  each note's filename [default: 3].
  --filenames                     Print the filename for each note.
  -n, --limit <limit>, --<limit>  The maximum number of notes to list.
  --no-id                         Don't include the id in list items.
  --no-indicator                  Don't include the indicator in list items.
  -p, --pager                     Display output in the pager.
  --paths                         Print the full path to each item.
  -s, --sort                      Order notes by id.
  -r, --reverse                   List items in reverse order.
  -t, --type <type>, --<type>     List items of <type>. <type> can be a file
                                  extension or one of the following types:
                                  archive, audio, book, bookmark, document,
                                  folder, image, note, text, video

Description:
  List notes in the current notebook.

  When <id>, <filename>, <path>, or <title> are present, the listing for the
  matching note will be displayed. When no match is found, titles and
  filenames will be searched for any that match <query> as a case-insensitive
  regular expression.

Indicators:
  🔉  Audio
  📖  Book
  🔖  Bookmark
  🔒  Encrypted
  📂  Folder
  🌄  Image
  📄  PDF, Word, or Open Office document
  📹  Video

Examples:
  ${_ME} list
  ${_ME} list example.md -e 10
  ${_ME} list --excerpt --no-id
  ${_ME} list --filenames --reverse
  ${_ME} list "^Example.*"
  ${_ME} list --10
  ${_ME} list --type document
  ${_ME} example:list
HEREDOC
_list() {
  local _size=
  _size="$(stty size)"

  local _columns="${_size##* }"

  if [[ -z "${_columns:-}" ]] || [[ "${_columns}" -lt 5 ]]
  then
    _columns="$(tput cols)"
  fi

  local _excerpt_length=3
  local _error_on_empty=0
  local _limit=
  local _list_color_enabled="${_COLOR_ENABLED}"
  local _list_filenames=0
  local _maybe_scope=
  local _maybe_scope_padded_right=
  local _no_id=0
  local _print_excerpt=0
  local _print_indicators=1
  local _list_paths=0
  local _reverse=0
  local _selector=
  local _filter_selectors=()
  local _sort=0
  local _type=
  local _use_pager=0

  while ((${#}))
  do
    local __arg="${1:-}"
    local __val="${2:-}"

    case "${__arg}" in
      -a|--all)
        _limit=
        ;;
      -e|--excerpt)
        _print_excerpt=1
        if _option_value_is_present "${__val:-}"
        then
          _excerpt_length="${__val:-}"
          shift
        fi
        ;;
      --filename*)
        _list_filenames=1
        ;;
      --error*on*empty|--hard*empty)
        _error_on_empty=1
        ;;
      --no*color)
        _list_color_enabled=0
        ;;
      --no-id*|--noid*|--no*index)
        _no_id=1
        ;;
      --no*indicator*|--no*emoji*|--no*unicode*)
        _print_indicators=0
        ;;
      -n|--limit|--num|--number)
        if _option_value_is_present "${__val:-}"
        then
          _limit="${__val:-}"
          shift
        fi
        ;;
      -p|--pager|--less)
        _use_pager=1
        ;;
      --path*)
        _list_paths=1
        ;;
      -s|--sort)
        _sort=1
        ;;
      -r|--reverse)
        _reverse=1
        ;;
      --titles)
        : # Do nothing. This is the default behavior.
        ;;
      -t|--type)
        _type="$(_option_get_value "${__arg}" "${__val:-}")"
        shift
        ;;
      *)
        if [[ "${__arg:-}" =~ ^--[0-9]+$ ]]
        then
          _limit="${__arg:2}"
        elif [[ "${__arg:-}" =~ ^-- ]]
        then
          _type="${__arg:2}"
        else
          if [[ -z "${_selector:-}" ]]
          then
            _selector="${__arg:-}"
          fi

          _filter_selectors+=("${__arg:-}")
        fi
        ;;
    esac

    shift
  done

  local _filter_pattern=
  _filter_pattern="$(_join "|" "${_filter_selectors[@]:-}")"

  local _selector_basename=
  local _selector_identifier=

  if [[ -n "${_selector:-}" ]]
  then
    _selector_basename="$(_show "${_selector}" --filename 2>/dev/null || :)"
    _selector_identifier="$(_show "${_selector}" --selector-id)"
  fi

  local _notebook_path=
  _notebook_path="$(_notebooks current --path)"

  local _notebook_escaped_name=
  _notebook_escaped_name="$(
    _notebooks show "${_notebook_path}" --escaped --no-color
  )"

  local _list_files_options=("${_notebook_path}")
  local _filenames=()

  [[ -n "${_type:-}" ]] && _list_files_options+=("--type" "${_type}")
  ((_reverse))          && _list_files_options+=("--reverse")
  ((_sort))             && _list_files_options+=("--sort")

  _filenames=($(_list_files "${_list_files_options[@]}")) || :

  local _filenames_count="${#_filenames[@]}"

  if _notebooks current --selected
  then
    _maybe_scope="$(_notebooks show "${_notebook_path}" --escaped --no-color):"
    _maybe_scope_padded_right="${_maybe_scope} "
  fi

  # Use code block to capture all output for piping to pager.
  {
    if ! ((_filenames_count)) && [[ -z "${_selector_identifier:-}" ]]
    then
      if ((_error_on_empty))
      then
        return 1
      elif [[ "${_type}" =~ ^bookmark$|^bookmarks$ ]]
      then
        cat <<HEREDOC
0 bookmarks.

Add a bookmark:
  $(_color_primary "${_ME} ${_maybe_scope_padded_right}<url>")
Help information:
  $(_color_primary "${_ME} help bookmark")
HEREDOC
      elif [[ -n "${_type:-}" ]]
      then
        if _contains "${_type}" "bookmarks" "documents" "images" "videos" "folders"
        then
          _type="$(printf "%s\\n" "${_type}" | sed -e 's/s$//')"
        fi

        cat <<HEREDOC
0 ${_type} files.

Import a file:
  $(_color_primary "${_ME} ${_maybe_scope}import (<path> | <url>)")
Help information:
  $(_color_primary "${_ME} help import")
HEREDOC
      else
        cat <<HEREDOC
0 items.

Add a note:
  $(_color_primary "${_ME} ${_maybe_scope}add")
Add a bookmark:
  $(_color_primary "${_ME} ${_maybe_scope_padded_right}<url>")
Import a file:
  $(_color_primary "${_ME} ${_maybe_scope}import (<path> | <url>)")
Help information:
  $(_color_primary "${_ME} help")
HEREDOC
      fi

      return 0
    else
      local _counter=0
      local _matches=0
      local _max_id
      _max_id="$(_index get_max_id)"

      for __basename in "${_filenames[@]:-}"
      do
        local _maybe_title=

        if [[ -n "${_selector_identifier:-}" ]]
        then
          # If a valid <selector> has been specified, skip the rest of the
          # loop unless the <selector> basename matches the current one.
          if [[ -n "${_selector_basename}" ]]
          then
            if [[ "${__basename}" != "${_selector_basename}" ]]
            then
              continue
            fi
          else
            # turn on a case-insensitive matching (-s set nocasematch)
            shopt -s nocasematch

            if [[ "${__basename:-}" =~ ${_filter_pattern} ]]
            then
              # include the note in the list
              _matches=$((_matches+1))
            else
              _maybe_title="$(_get_title "${_notebook_path}/${__basename}")"

              if [[ "${_maybe_title:-}" =~ ${_filter_pattern} ]]
              then
                # include the note in the list
                _matches=$((_matches+1))
              else
                continue # don't include note in list
              fi
            fi

            # turn off a case-insensitive matching (-u unset nocasematch)
            shopt -u nocasematch
          fi
        fi

        if [[ -n "${_limit:-}" ]] && [[ "${_counter}" -ge "${_limit}" ]]
        then # the limit has been reached.
          if [[ -n "${_selector_identifier:-}" ]]
          then
            # keep looking for matches to count total
            continue
          else
            break
          fi
        fi

        local _title_basename_or_path="${__basename}"

        if ((_list_paths))
        then
          _title_basename_or_path="${_notebook_path}/${__basename}"
        elif ! ((_list_filenames))
        then
          if [[ -z "${_maybe_title}" ]]
          then
            _maybe_title="$(_get_title "${_notebook_path}/${__basename}")"
          fi

          if [[ -n "${_maybe_title}" ]]
          then
            _title_basename_or_path="${_maybe_title}"
          fi
        fi

        local _extra_length=0
        local _indicators=

        if ((_print_indicators))
        then
          # TODO: `show --indicators` duplicated for performance
          if _file_is_bookmark "${_notebook_path}/${__basename}"
          then
            _indicators+="🔖 "
          elif ! LC_ALL=C _contains "${__basename##*.}" "${_TEXT_FILE_EXTENSIONS[@]}"
          then
            if _file_is_image "${_notebook_path}/${__basename}"
            then
              _indicators+="🌄 "
            elif _file_is_document "${_notebook_path}/${__basename}"
            then
              _indicators+="📄 "
            elif [[ -d "${_notebook_path}/${__basename}" ]]
            then
              _indicators+="📂 "
            elif _file_is_video "${_notebook_path}/${__basename}"
            then
              _indicators+="📹 "
            elif _file_is_audio "${_notebook_path}/${__basename}"
            then
              _indicators+="🔉 "
            elif [[ "${__basename}" =~ \.epub$ ]]
            then
              _indicators+="📖 "
            fi

            if _file_is_encrypted "${_notebook_path}/${__basename}"
            then
              _indicators+="🔒 "
            fi
          fi

          if [[ "${_title_basename_or_path}" =~ todo|Todo|TODO ]]
          then
            _indicators+="✅ "
            _extra_length=1
          fi
        fi

        local _item_info="${_indicators}${_title_basename_or_path}"

        if ! ((_list_filenames))  &&
           ! ((_list_paths))      &&
           ! ((_print_excerpt))   &&
           [[ "${_title_basename_or_path}" == "${__basename}" ]]
        then
          local _first_line=
          _first_line="$(_get_first_line "${_notebook_path}/${__basename}")"

          if [[ -n "${_first_line:-}" ]]
          then
            _item_info="${_item_info} · \"${_first_line}\""
          fi
        fi

        local _info_line=
        local _info_line_color=

        if ((_no_id))
        then
          _info_line="${_item_info}"
          _info_line_color="${_item_info}"
        else
          local _item_identifier=
          # use `sed` directly instead of `_index get_id` for performance
          _item_identifier="$(
            sed -n "/^${__basename:-}$/=" "${_notebook_path:-}/.index"
          )"

          local _max_identifier="${_max_id}"

          if [[ -n "${_maybe_scope:-}" ]]
          then
            _item_identifier="${_notebook_escaped_name}:${_item_identifier}"
            _max_identifier="${_notebook_escaped_name}:${_max_id}"
          fi

          local _brackets_identifier="[${_item_identifier}]"

          # avoid calling `_color_brackets` for performance
          local _brackets_identifier_color="\
${_TPUT_SGR0}\
${_TPUT_SETAF_8}\
[\
${_TPUT_COLOR_PRIMARY}\
${_item_identifier:-}\
${_TPUT_SETAF_8}\
]\
${_TPUT_SGR0}"

          # use calculated number of spaces for nicer formatting
          local _spaces_length=
          _spaces_length=$(( ${#_max_identifier} - ${#_item_identifier} + 1 ))

          local _spaces=
          printf -v _spaces '%*s' "${_spaces_length}" ""

          _info_line="${_brackets_identifier}${_spaces}${_item_info}"
          _info_line_color="${_brackets_identifier_color}${_spaces}${_item_info}"
        fi

        if ((_list_color_enabled))  &&
           ! ((_list_filenames))    &&
           ! ((_list_paths))        &&
           ! ((_no_id))
        then
          _wrap off
        fi

        if ((_list_color_enabled))
        then
          printf "%s" "${_info_line_color}"
        else
          printf "%s" "${_info_line}"
        fi

        if ((_list_color_enabled))  &&
           ! ((_list_filenames))    &&
           ! ((_list_paths))        &&
           ! ((_no_id))
        then
          _wrap on
        fi

        # end of line
        printf "\\n"

        if ((_print_excerpt))                               &&
           [[ "${_excerpt_length}" =~ ^[1-9] ]]             &&
           _file_is_text "${_notebook_path}/${__basename}"
        then # excerpt is specified with a non-zero digit, required by `head`
          local _visible_info_line_length=
          _visible_info_line_length="$(
            _get_visible_length "${_info_line:-}" "${_extra_length:-0}"
          )"

          if [[ -z "${_visible_info_line_length:-}"               ]] ||
             [[ "${_visible_info_line_length}" -gt "${_columns}"  ]]
          then
            _print_line "$(printf "%-${_columns}s" '.')" | tr -d '\n'
          else
            _print_line "$(printf "%-${_visible_info_line_length}s" '.')"
          fi
          head -"${_excerpt_length}" "${_notebook_path}/${__basename}" \
            | _highlight_syntax_if_available "${__basename##*.}"
          printf "\\n"
        fi

        _counter=$((_counter+1))
      done

      if [[ -n "${_limit:-}" ]]
      then
        if [[ -n "${_selector_identifier:-}" ]]
        then
          if [[ "${_matches:-0}" -gt "${_limit}" ]]
          then
            local _label="matches"
            local _omitted_count
            _omitted_count=$((_matches-_counter))

            [[ "${_omitted_count}" -eq 1 ]] && _label="match"

            printf "%s %s omitted. %s total.\\n"  \
              "${_omitted_count}"                 \
              "${_label}"                         \
              "${_matches}"
            return 0
          fi

        elif [[ "${_filenames_count}" -gt "${_limit}" ]]
        then
          printf "%s omitted. %s total.\\n"   \
            "$((_filenames_count-_counter))"  \
            "${_filenames_count}"
          return 0
        fi
      fi

      if ! ((_counter)) && [[ -n "${_selector:-}" ]]
      then
        local _filter_message=
        _filter_message="$(_color_primary "${_selector}")"

        if [[ -n "${_type:-}" ]]
        then
           _filter_message="${_filter_message} Type: $(_color_primary "${_type}")"
        fi

        _warn \
          printf "Not found: %s\\n" "${_filter_message}" &&
          return 1
      fi
    fi
  } | if ((_use_pager))
      then
        _pager
      else
        cat
      fi
}

# ls ####################################################################### ls

describe "ls" <<HEREDOC
Usage:
  ${_ME} ls [-a | --all] [-e [<length>] | --excerpt [<length>]] [--filenames]
        [-n <limit> | --limit <limit> | --<limit>] [--no-id] [--no-indicator]
        [-p | --pager] [--paths] [-s | --sort] [-r | --reverse]
        [-t <type> | --type <type> | --<type>]
        [<id> | <filename> | <path> | <title> | <query>]

Options:
  -a, --all                       Print all items in the notebook. Equivalent
                                  to no limit.
  -e, --excerpt [<length>]        Print an excerpt <length> lines long under
                                  each note's filename [default: 3].
  --filenames                     Print the filename for each note.
  -n, --limit <limit>, --<limit>  The maximum number of listed items.
                                  [default: 20]
  --no-id                         Don't include the id in list items.
  --no-indicator                  Don't include the indicator in list items.
  -p, --pager                     Display output in the pager.
  --paths                         Print the full path to each item.
  -s, --sort                      Order notes by id.
  -r, --reverse                   List items in reverse order.
  -t, --type <type>, --<type>     List items of <type>. <type> can be a file
                                  extension or one of the following types:
                                  note, book, bookmark, document, archive,
                                  image, video, audio, folder, text

Description:
  List notebooks and notes in the current notebook, displaying note titles
  when available. \`${_ME} ls\` is a combination of \`${_ME} notebooks\` and
  \`${_ME} list\` in one view.

  When <id>, <filename>, <path>, or <title> are present, the listing for the
  matching note will be displayed. When no match is found, titles and
  filenames will be searched for any that match <query> as a case-insensitive
  regular expression.

  Options are passed through to \`list\`. For more information, see
  \`${_ME} help list\`.

Indicators:
  🔉  Audio
  📖  Book
  🔖  Bookmark
  🔒  Encrypted
  📂  Folder
  🌄  Image
  📄  PDF, Word, or Open Office document
  📹  Video

Examples:
  ${_ME}
  ${_ME} --all
  ${_ME} ls
  ${_ME} ls example.md -e 10
  ${_ME} ls --excerpt --no-id
  ${_ME} ls --reverse
  ${_ME} ls "^Example.*"
  ${_ME} ls --10
  ${_ME} ls --type document
  ${_ME} example:
  ${_ME} example: -ae
  ${_ME} example:ls
HEREDOC
_ls() {
  # _ls_print_footer()
  #
  # Usage:
  #   _ls_print_footer <notebook>...
  _ls_print_footer() {
    local _footer_line_hi=
    local _footer_line_raw=
    local _footer_separator_raw=" ·"
    local _footer_separator_hi=
    _footer_separator_hi="$(_color_primary "${_footer_separator_raw}")"
    local _footer_test_line=
    local _maybe_scope=
    local _maybe_scope_padded_left=
    local _maybe_scope_padded_right=
    local _notebook_names=("${@:-}")

    if _notebooks current --selected
    then
      _maybe_scope="$(
        _notebooks show "$(_notebooks current)" --escaped --no-color
      ):"

      _maybe_scope_padded_left=" ${_maybe_scope}"
      _maybe_scope_padded_right="${_maybe_scope} "
    fi

    local _notebook_command="${_ME} notebooks add <name>"
    if [[ "${#_notebook_names[@]}" -gt 1 ]]
    then
      if [[ -n "${_maybe_scope:-}" ]]
      then
        _notebook_command="${_ME} use ${_maybe_scope%%:*}"
      else
        _notebook_command="${_ME} use <notebook>"
      fi
    fi

    local _commands_raw=(
      "❯ ${_ME}${_maybe_scope_padded_left}"
      "${_ME} ${_maybe_scope}add"
      "${_ME} ${_maybe_scope_padded_right}<url>"
      "${_ME} edit ${_maybe_scope}<id>"
      "${_ME} show ${_maybe_scope}<id>"
      "${_ME} open ${_maybe_scope}<id>"
      "${_ME} ${_maybe_scope}list"
      "${_ME} ${_maybe_scope}search <query>"
      "${_notebook_command}"
      "${_ME} settings"
      "${_ME} help"
    )

    local _commands_hi=(
      "$(_color_dim '❯') $(_color_secondary "${_ME}${_maybe_scope_padded_left}")"
      "$(_color_secondary "${_commands_raw[1]}")"
      "$(_color_secondary "${_commands_raw[2]}")"
      "$(_color_secondary "${_commands_raw[3]}")"
      "$(_color_secondary "${_commands_raw[4]}")"
      "$(_color_secondary "${_commands_raw[5]}")"
      "$(_color_secondary "${_commands_raw[6]}")"
      "$(_color_secondary "${_commands_raw[7]}")"
      "$(_color_secondary "${_commands_raw[8]}")"
      "$(_color_secondary "${_commands_raw[9]}")"
      "$(_color_secondary "${_commands_raw[10]}")"
    )

    _wrap off
    _print_line "$(printf "%-${_columns}s" '.')" | tr -d '\n'
    printf "%s" "${_TPUT_SGR0}"
    _wrap on
    printf "\\n"

    for ((i=0; i < "${#_commands_raw[@]}"; i++))
    do
      if [[ -z "${_footer_test_line}" ]]
      then # first item in line
        _footer_test_line="${_commands_raw[i]}"
      else
        _footer_test_line="${_footer_test_line}${_footer_separator_raw} ${_commands_raw[i]}"
      fi

      if [[ "${#_footer_test_line}" -gt "${_columns}" ]]
      then # wrap to next line
        printf "%s\\n" "${_footer_line_hi}"

        # Start the line for the next iteration using the current name.
        _footer_test_line="${_commands_raw[i]}"
        _footer_line_hi="${_commands_hi[i]:-}"
        _footer_line_raw="${_commands_raw[i]}"
      else # add to line
        if [[ -z "${_footer_line_hi}" ]]
        then # first item in highlighted line
          _footer_line_hi="${_commands_hi[i]:-}"
          _footer_line_raw="${_commands_raw[i]}"
        else
          _footer_line_hi="${_footer_line_hi}${_footer_separator_hi} ${_commands_hi[i]:-}"
          _footer_line_raw="${_footer_line_raw}${_footer_separator_raw} ${_commands_raw[i]}"
        fi
      fi

      if [[ "${#_commands_raw[@]}" == $((i+1)) ]]
      then # end of list
        printf "%s\\n" "${_footer_line_hi}"
      fi
    done
  }

  # _ls_print_header()
  #
  # Usage:
  #   _ls_print_header <alignment> <notebook>...
  #
  # Description:
  #   Build word-wrapping notebook line. `fold` doesn't account for
  #   highlighting.
  #
  #   Simple version:
  #     _notebooks --names            \
  #       | tr '\n' ' '               \
  #       | fold -s "-w$(tput cols)"  \
  #       && printf "\\n"
  _ls_print_header() {
    local _alignment="${1:-}"
    shift

    local _auto_align=1 # auto-align by default.
    local _centered=0
    local _justified=0
    local _line_number=0
    local _line_hi=
    local _line_raw=
    local _notebook_names=("${@:-}")
    local _output_hi=
    local _output_raw=
    local _padding=
    local _plain=0
    local _test_line=

    [[ "${NB_HEADER}" == 2 ]] && _centered=1
    [[ "${NB_HEADER}" == 3 ]] && _justified=1

    case "${_alignment}" in
      --auto*align)
        _auto_align=1
        _centered=0
        _justified=0
        ;;
      --centered)
        _auto_align=0
        _centered=1
        _justified=0
        ;;
      --justified)
        _auto_align=0
        _centered=0
        _justified=1
        ;;
      --plain)
        _plain=1
        ;;
    esac

    local _separator=
    local _separator_hi=

    if ! ((_plain))
    then
      _separator=" ·"
      _separator_hi="$(_color_dim "${_separator}")"
    fi

    local _current_notebook_name=
    _current_notebook_name="$(_notebooks current)"

    local _notebook_names_archived=
    _notebook_names_archived=($(
      _notebooks --names --no-color --archived || :
    ))
    local _archived_count="${#_notebook_names_archived[@]}"

    if ((_archived_count))
    then
      _notebook_names=(
        "${_notebook_names[@]}"
        "[${_archived_count} archived]"
      )
    fi

    if _notebooks current --local
    then
      _notebook_names=(
        "local"
        "${_notebook_names[@]}"
      )
    fi

    for ((i=0; i < "${#_notebook_names[@]}"; i++))
    do
      local _maybe_highlighted_notebook_name=

      if [[ "${_current_notebook_name}" == "${_notebook_names[i]}" ]]
      then
        _maybe_highlighted_notebook_name="$(
          _color_primary "${_notebook_names[i]:-}" --underline
        )"
      else
        _maybe_highlighted_notebook_name="${_notebook_names[i]:-}"
      fi

      if [[ -z "${_test_line}" ]]
      then # first item in line
        _test_line="${_notebook_names[i]}"
      else
        _test_line="${_test_line}${_separator} ${_notebook_names[i]}"
      fi

      if [[ "${#_test_line}" -gt "${_columns}" ]]
      then # wrap to next line
        # Use the existing value of $_line_[highlighted|raw] without the
        # current name.
        if ((_auto_align)) || ((_centered)) && ! ((_justified))
        then
          _centered=1
          _padding="$(
            _print_padding "${_line_raw}" "${_columns}" "${_centered}"
          )"
        fi

        _output_hi="${_output_hi}${_padding}${_line_hi}\\n"
        _output_raw="${_output_raw}${_padding}${_line_raw}\\n"
        _line_number=$((_line_number + 1))

        # Start the line for the next iteration using the current name.
        _test_line="${_notebook_names[i]}"
        _line_hi="${_maybe_highlighted_notebook_name:-}"
        _line_raw="${_notebook_names[i]}"
      else # add to line
        if [[ -z "${_line_hi}" ]]
        then # first item in highlighted line
          _line_hi="${_maybe_highlighted_notebook_name:-}"
          _line_raw="${_notebook_names[i]}"
        else
          _line_hi="${_line_hi}${_separator_hi} ${_maybe_highlighted_notebook_name:-}"
          _line_raw="${_line_raw}${_separator} ${_notebook_names[i]}"
        fi
      fi

      if [[ "${#_notebook_names[@]}" == $((i+1)) ]]
      then # end of list
        if ((_line_number))  && ! ((_justified)) &&
           ((_auto_align))   ||   ((_centered))
        then
          _centered=1
        fi
        _padding="$(
          _print_padding "${_line_raw}" "${_columns}" "${_centered}"
        )"

        _output_hi="${_output_hi}${_padding}${_line_hi}\\n"
        _output_raw="${_output_raw}${_padding}${_line_raw}\\n"
        _line_number=$((_line_number + 1))
      fi
    done

    printf "%b" "${_output_hi}"

    _wrap off

    if ((_centered))
    then # print full-width line.
      _print_line "$(printf "%-${_columns}s" '.')" | tr -d '\n'
    else # print line the length of the first line.
      _print_line "$(
        # $_output_raw generates a line 2 characters too long. TODO: Review
        printf "%s" "${_output_raw}" | sed 's/..$//'
      )" | tr -d '\n'
    fi

    printf "%s" "${_TPUT_SGR0}"
    _wrap on
    printf "\\n"
  }

  local _all=0
  local _arguments=()
  local _columns
  _columns="$(tput cols)"
  local _header_flags=()
  local _limit=
  local _list_options=()
  local _notebook_names
  _notebook_names=($(
    _notebooks --names --no-color --unarchived --global || :
  ))
  local _selector=
  local _type=
  local _use_pager=0

  while ((${#}))
  do
    local __arg="${1:-}"
    local __val="${2:-}"

    case "${__arg}" in
      -a|--all)
        _all=1
        ;;
      --auto*align|--centered|--justified|--plain)
        _header_flags+=("${__arg}")
        ;;
      -n|--limit|--num|--number)
        if _option_value_is_present "${__val:-}"
        then
          _limit="${__val:-}"
        fi

        shift
        ;;
      -p|--pager|--less)
        _use_pager=1
        ;;
      -l)
        : # Ignore accidental ls -l  due to muscle memory.
        ;;
      -t|--type)
        _type="$(_option_get_value "${__arg}" "${__val:-}")"
        _arguments+=("${__arg}" "${__val:-}")
        shift
        ;;
      --[0-9]*)
        if [[ "${__arg:-}" =~ ^--[0-9]+$ ]]
        then
          _limit="${__arg#\-\-}"
        else
          _arguments+=("${__arg}")
        fi
        ;;
      --[A-Za-z]*)
        _type="${__arg:2}"
        _arguments+=("${__arg}")
        ;;
      -*)
        _arguments+=("${__arg}")

        if _option_value_is_present "${__val:-}"
        then
          _arguments+=("${__val:-}")

          shift
        fi
        ;;
      *:*)
        _arguments+=("$(_show "${__arg}" --selector-id)")
        _selector="${__arg#*:}"
        ;;
      *)
        if [[ -z "${_selector:-}" ]]
        then
          _selector="${__arg}"
        fi

        _arguments+=("${__arg}")
        ;;
    esac

    shift
  done

  _list_options=(${_arguments[@]:-})

  if [[ -n "${_limit:-}" ]]
  then
    _list_options+=("--limit" "${_limit}")
  elif [[ -z "${_selector:-}" ]] && ! ((_all))
  then
    _list_options+=("--limit" "${NB_LIMIT}")
  fi

  {
    if ((NB_HEADER)) && [[ -z "${_arguments[*]:-}" ]]
    then
      _ls_print_header "${_header_flags[@]:-}" "${_notebook_names[@]:-}"
    fi

    _list --error-on-empty "${_list_options[@]:-}" 2>/dev/null || {
      if [[ -n "${_arguments[*]:-}" ]]
      then
        local _maybe_notebook_name="${_arguments[0]:-}"

        _maybe_notebook_name="$(
          printf "%s\\n" "${_maybe_notebook_name}" | sed 's/\:$//'
        )"

        local _notebook_name=
        _notebook_name="$(
          _notebooks \
            list "${_maybe_notebook_name:-}" --names --no-color 2>/dev/null
        )" || :
      fi

      if [[ -n "${_notebook_name:-}" ]] && ! _notebooks current --selected
      then
        NB_NOTEBOOK_PATH="${NB_DIR}/${_notebook_name}"  \
          _ls "${_arguments[@]:1}"
        return 0
      else
        local _local_count
        _local_count="$(_count)"

        if ! ((_local_count)) || [[ -z "${_selector:-}" ]]
        then
          _list "${_list_options[@]:-}"
        else
        local _filter_message=
        _filter_message="${_selector}"

          if _notebooks current --selected
          then
            _filter_message="$(_notebooks current --name):${_filter_message}"
          fi

          _filter_message="$(_color_primary "${_filter_message}")"

          if [[ -n "${_type:-}" ]]
          then
             _filter_message="${_filter_message} Type: $(_color_primary "${_type}")"
          fi

          _warn printf "Not found: %s\\n" "${_filter_message}" &&
            return 1
        fi
      fi
    }

    if ((NB_FOOTER)) && [[ -z "${_arguments[*]:-}" ]]
    then
      _ls_print_footer "${_notebook_names[@]:-}"
    fi
  } | if ((_use_pager))
      then
        _pager
      else
        cat
      fi
}

# move ################################################################### move

describe "move" <<HEREDOC
Usage:
  ${_ME} move (<id> | <filename> | <path> | <title>) [-f | --force] <notebook>

Options:
  -f, --force   Skip the confirmation prompt.

Description:
  Move the specified note to <notebook>.

Examples:
  ${_ME} move 1 example-notebook
  ${_ME} move example.md example-notebook
  ${_ME} example:move sample.md other-notebook
  ${_ME} move example:sample.md other-notebook
  ${_ME} mv 1 example-notebook

Shortcut Alias: \`mv\`
HEREDOC
_move() {
  local _basename=
  local _force=0
  local _selector=
  local _target_notebook=

  for __arg in "${@:-}"
  do
    case "${__arg}" in
      -f|--force)
        _force=1
        ;;
      *)
        if [[ -z "${_selector}" ]]
        then
          _selector="${__arg}"
        elif [[ -z "${_target_notebook}" ]]
        then
          _target_notebook="${__arg}"
        fi
        ;;
    esac
  done

  if [[ -z "${_selector:-}" ]] || [[ -z "${_target_notebook:-}" ]]
  then
    _exit_1 _help "move"
  fi

  if [[ "${_target_notebook}" =~ :$ ]]
  then
    _target_notebook="$(printf "%s\\n" "${_target_notebook}" | tr -d ':')"
  fi

  local _basename
  _basename="$(_show "${_selector}" --filename)"

  local _notebook_escaped_name
  _notebook_escaped_name="$(
    _notebooks show "$(_notebooks current)" --escaped --no-color
  )"

  local _notebook_path
  _notebook_path="$(_notebooks current --path)"

  local _target_notebook_path
  _target_notebook_path="$(
    _notebooks show "${_target_notebook}" --path 2>/dev/null || :
  )"

  local _target_notebook_escaped_name
  _target_notebook_escaped_name="$(
    _notebooks show "${_target_notebook}" --escaped --no-color 2>/dev/null || :
  )"

  if [[ -z "${_basename}"                             ]] ||
     [[ ! -e "${_notebook_path}/${_basename}"         ]]
  then
    _exit_1 printf        \
      "Not found: %s\\n"  \
      "$(_color_primary "${_notebook_escaped_name}:${_selector}")"
  elif [[ ! -e "${_target_notebook_path}/.git"        ]]
  then
    _exit_1 printf                        \
      "Target notebook not found: %s\\n"  \
      "$(_color_primary "${_target_notebook}")"
  elif [[ -e "${_target_notebook_path}/${_basename}"  ]]
  then
    _exit_1 printf            \
      "Already exists: %s\\n" \
      "$(_color_primary "${_target_notebook_escaped_name}:${_basename}")"
  fi

  _source_info="$(_show "${_selector}" --info-line)"

  if ! ((_force))
  then
    printf "Moving:  %s\\n"  "${_source_info}"
    printf "To:       %s\\n" "$(_color_primary "${_target_notebook}")"

    while true
    do
      IFS='' read -r -e -d $'\n' -p "\
$(_color_primary "Proceed?") $(_color_brackets "y/N") " __yn

      case ${__yn} in
        [Yy]*)
          break
          ;;
        *)
          printf "Exiting...\\n"
          exit 0
          ;;
      esac
    done
  fi

  if [[ -d "${_notebook_path}/${_basename}" ]]
  then
    NB_NOTEBOOK_PATH="${_target_notebook_path}"     \
      _import "${_notebook_path}/${_basename}" 1> /dev/null
  else
    cat "${_notebook_path}/${_basename}"            \
      | NB_NOTEBOOK_PATH="${_target_notebook_path}" \
        _add "${_basename}" 1> /dev/null
  fi

  NB_NOTEBOOK_PATH="${_notebook_path}"              \
    _delete "${_basename}" --force 1> /dev/null

  local _id
  _id="$(
    NB_NOTEBOOK_PATH="${_target_notebook_path}" _index get_id "${_basename}"
  )"

  if [[ ! -e "${_notebook_path}/${_basename}"       ]] ||
     [[ -e "${_target_notebook_path}/${_basename}"  ]]
  then
    printf "Moved to %s %s\\n"                                \
      "$(_color_brackets "${_target_notebook}:${_id}")"       \
      "$(_color_primary "${_target_notebook}:${_basename}")"
  fi
}
_alias_subcommand "move" "mv"

# notebooks ######################################################### notebooks

# TODO: Vim highlighting bug. "

describe "notebooks" <<HEREDOC
Usage:
  ${_ME} notebooks [<name>] [--archived] [--global] [--local] [--names]
               [--paths] [--unarchived]
  ${_ME} notebooks add <name> [<remote-url>]
  ${_ME} notebooks (archive | open | peek | status | unarchive) [<name>]
  ${_ME} notebooks current [--path | --selected | --filename [<filename>]]
                       [--global | --local]
  ${_ME} notebooks delete <name> [-f | --force]
  ${_ME} notebooks (export <name> [<path>] | import <path>)
  ${_ME} notebooks init [<path> [<remote-url>]]
  ${_ME} notebooks rename <old-name> <new-name>
  ${_ME} notebooks select <selector>
  ${_ME} notebooks show (<name> | <path> | <selector>) [--archived]
                    [--escaped | --name | --path | --filename [<filename>]]
  ${_ME} notebooks use <name>

Options:
  --archived               List archived notebooks, or return archival status
                           with \`show\`.
  --escaped                Print the notebook name with spaces escaped.
  --filename [<filename>]  Print an available filename for the notebooks. When
                           <filename> is provided, check for an existing file
                           and provide a filename with an appended sequence
                           number for uniqueness.
  --global                 List global notebooks or the notebook set globally
                           with \`use\`.
  --local                  Exit with 0 if current within a local notebook,
                           otherwise exit with 1.
  -f, --force              Skip the confirmation prompt.
  --name, --names          Print the notebook name.
  --path, --paths          Print the notebook path.
  --selected               Exit with 0 if the current notebook differs from
                           the current global notebook, otherwise exit with 1.
  --unarchived             Only list unarchived notebooks.

Subcommands:
  (default)  List notebooks.
  add        Create a new global notebook. When an existing notebook's
             <remote-url> is specified, create the new global notebook as a
             clone of <remote-url>.
             Aliases: \`notebooks create\`, \`notebooks new\`
  archive    Set the current notebook or notebook <name> to "archived" status.
  export     Export the notebook <name> to the current directory or <path>,
             making it usable as a local notebook.
  import     Import the local notebook at <path> to make it global.
  init       Create a new local notebook. Specify a <path> or omit to
             initialize the current working directory as a local notebook.
             Specify <remote-url> to clone an existing notebook.
  current    Print the current notebook name or path.
  delete     Delete a notebook.
  open       Open the current notebook directory or notebook <name> in your
             file browser, explorer, or finder.
             Shortcut Alias: \`o\`
  peek       Open the current notebook directory or notebook <name> in the
             first tool found in the following list:
             \`ranger\` [1], \`mc\` [2], \`exa\` [3], or \`ls\`.
             Shortcut Alias: \`p\`
  rename     Rename a notebook.
  select     Set the current notebook from a colon-prefixed selector.
             Not persisted. Selection format: <notebook>:<identifier>
  status     Print the archival status of the current notebook or
             notebook <name>.
  show       Show and return information about a specified notebook.
  unarchive  Remove "archived" status from current notebook or notebook <name>.
  use        Switch to a notebook.

    1. https://ranger.github.io/
    2. https://en.wikipedia.org/wiki/Midnight_Commander
    3. https://github.com/ogham/exa

Description:
  Manage notebooks.

Examples:
  ${_ME} notebooks --names
  ${_ME} notebooks add sample
  ${_ME} notebooks add example https://github.com/example/example.git
  ${_ME} n current --path
  ${_ME} n archive example

Shortcut Alias: \`n\`
HEREDOC
_notebooks() {
  # _notebooks_add()
  #
  # Usage:
  #   _notebooks_add <name> [<remote_url>]
  _notebooks_add() {
    local _name="${1:-}"
    if [[ -z "${_name}" ]]
    then
      _exit_1 _help notebooks
    fi

    if [[ -e "${NB_DIR}/${_name}" ]]
    then
      _exit_1 printf "Already exists: %s\\n" "$(_color_primary "${_name}")"
    fi

    local _repo_url="${2:-}"
    if [[ -n "${_repo_url}" ]]
    then
      git clone "${_repo_url}" "${NB_DIR}/${_name}" &&
        printf "%s\\n" "${_name}" > "${NB_DIR}/.current"
    else
      mkdir -p "${NB_DIR}/${_name}"
        git -C "${NB_DIR}/${_name}" init 1>/dev/null  &&
        touch "${NB_DIR}/${_name}/.index"             &&
        _temp cache clear                             &&
        _git checkpoint "${NB_DIR}/${_name}" "[nb] Initialize"
    fi && printf "Added notebook: %s\\n" "$(_color_primary "${_name}")"
  }

  # _notebooks_current()
  #
  # Usage:
  #   _notebooks_current (<name> | <path> | <selector>) [--global | --local]
  #                      [--path | --scoped]
  _notebooks_current() {
    local _filename=
    local _print_global=0
    local _print_local=0
    local _print_path=0
    local _print_unique_filename=0
    local _return_selected=0
    local _selector=

    while ((${#}))
    do
      local __arg="${1:-}"
      local __val="${2:-}"

      case "${__arg:-}" in
        --global)
          _print_global=1
          ;;
        --local)
          _print_local=1
          ;;
        --path*)
          _print_path=1
          ;;
        --scoped|--selected)
          _return_selected=1
          ;;
        --filename|--basename)
          _print_unique_filename=1

          if _option_value_is_present "${__val:-}"
          then
            _filename="${__val}"
            shift
          fi
          ;;
        [^-]*)
          _selector="${__arg}"
          ;;
      esac

      shift
    done

    if [[ -n "${_selector:-}" ]]
    then
      _notebooks_select "${_selector}" || :
    fi

    if ((_print_local))
    then
      if [[ -n "${_LOCAL_NOTEBOOK_PATH:-}" ]]
      then
        if ((_print_path))
        then
          printf "%s\\n" "${_LOCAL_NOTEBOOK_PATH}"
        else
          return 0
        fi
      else
        return 1
      fi
    elif ((_print_global))
    then
      if ((_print_path))
      then
        printf "%s\\n" "${_GLOBAL_NOTEBOOK_PATH}"
      else
        basename "${_GLOBAL_NOTEBOOK_PATH}"
      fi
    elif ((_print_path))
    then
      printf "%s\\n" "${NB_NOTEBOOK_PATH}"
    elif ((_return_selected))
    then
      [[ "${NB_NOTEBOOK_PATH}" != "${_GLOBAL_NOTEBOOK_PATH}" ]] &&
      [[ "${NB_NOTEBOOK_PATH}" != "${_LOCAL_NOTEBOOK_PATH}"  ]]
    elif ((_print_unique_filename))
    then
      _notebooks_show "${NB_NOTEBOOK_PATH}" --filename "${_filename:-}"
    else
      if [[ -n "${_LOCAL_NOTEBOOK_PATH:-}"                      ]] &&
         [[ "${NB_NOTEBOOK_PATH}" == "${_LOCAL_NOTEBOOK_PATH}"  ]]
      then
        printf "local\\n"
      else
        basename "${NB_NOTEBOOK_PATH}"
      fi
    fi
  }

  # _notebooks_delete()
  #
  # Usage:
  #   _notebooks_delete <name>
  _notebooks_delete() {
    local _force=0
    local _name=

    for __arg in "${@:-}"
    do
      case "${__arg}" in
        -f|--force)
          _force=1
          ;;
        *)
          [[ -z "${_name:-}" ]] && _name="${__arg%%:*}"
          ;;
      esac
    done

    if [[ -z "${_name}" ]]
    then
      _exit_1 _help notebooks
    elif [[ "${_name}" == "local"          ]] &&
         [[ -n "${_LOCAL_NOTEBOOK_PATH:-}" ]]
    then
      _exit_1 cat <<HEREDOC
Use your system's shell or file explorer to delete local notebook directories.
HEREDOC
    fi

    if [[ ! -d "${NB_DIR}/${_name}"        ]] ||
       [[ ! -e "${NB_DIR}/${_name}/.git"   ]] ||
       [[ ! -e "${NB_DIR}/${_name}/.index" ]]
    then
      _exit_1 printf "Notebook not found: %s\\n" "$(_color_primary "${_name}")"
    fi

    local _notebook_names=()
    _notebook_names=($(_notebooks --names --no-color))

    local _notebook_count=
    _notebook_count="${#_notebook_names[@]}"

    if ! ((_force))
    then
      if _command_exists "trash" && [[ "${OSTYPE}" =~ ^darwin ]]
      then
        printf "Moving to Trash: %s\\n" "$(_color_primary "${_name}")"
      else
        cat <<HEREDOC
Deleting $(_color_primary "${_name}").

This action cannot be undone. This will permanently delete the
$(_color_primary "${_name}") notebook, all notes and files it contains,
and the entire revision history.

HEREDOC
      fi

      while true
      do
        IFS='' read -r -e -d $'\n' -p "\
Please type $(_color_primary "${_name}") to confirm: " __response

        if [[ "${__response}" == "${_name}" ]]
        then
          break
        else
          printf "Exiting...\\n"
          exit 0
        fi
      done
    fi

    if [[ "$(_notebooks current)" == "${_name}" ]] &&
       [[ "${_notebook_count}" -gt 1            ]]
    then
      if [[ "${_name}" != "home" ]]
      then
        _notebooks use "home"
      else
        for __notebook in "${_notebook_names[@]:-}"
        do
          if [[ "${__notebook}" != "$(_notebooks current)" ]]
          then
            _notebooks use "${__notebook}"
            break
          fi
        done
      fi
    fi

    if _command_exists "trash" && [[ "${OSTYPE}" =~ ^darwin ]]
    then
      trash "${NB_DIR:?}/${_name:?}"
    else
      rm -r -f "${NB_DIR:?}/${_name:?}"
    fi &&
      _temp cache clear &&
      printf "Notebook deleted: %s\\n" "$(_color_primary "${_name}")"
  }

  # _notebooks_export()
  #
  # Usage:
  #   _notebooks_export <name> [<path>]
  _notebooks_export() {
    local _name="${1:-}"
    if [[ -z "${_name:-}" ]]
    then
      _exit_1 _help "notebooks"
    fi

    if [[ ! -d "${NB_DIR}/${_name}" ]]
    then
      _exit_1 printf "Notebook not found: %s\\n" "$(_color_primary "${_name}")"
    fi

    local _target_path="${2:-}"

    if [[ -n "${_target_path:-}" ]] && [[ ! "${_target_path}" =~ ^/ ]]
    then
      _target_path="${_CURRENT_WORKING_DIR}/${_target_path}"
    fi

    if [[ -n "${_target_path:-}"              ]] &&
       [[ -d "${_target_path}"                ]] &&
       [[ ! "${_target_path}" =~ \/${_name}$  ]]
    then
      _target_path="${_target_path}/${_name}"
    fi

    if [[ -z "${_target_path}" ]]
    then
      _target_path="${_CURRENT_WORKING_DIR}/${_name}"
    fi

    _target_path="$(_get_unique_path "${_target_path}")"

    cp -R "${NB_DIR}/${_name}" "${_target_path}"  &&
      _temp cache clear                           &&
      printf "Exported notebook %s to %s\\n"  \
        "$(_color_primary "${_name}")"        \
        "$(_color_primary "${_target_path}")"
  }

  # _notebooks_import()
  #
  # Usage:
  #   _notebooks_import <path> [<name>]
  _notebooks_import() {
    local _path="${1:-}"
    if [[ -z "${_path:-}" ]]
    then
      _exit_1 _help "notebooks"
    fi

    local _basename=
    _basename="$(basename "${_path}")"

    if [[ ! "${_path}" =~ ^/ ]]
    then
      _path="${_CURRENT_WORKING_DIR}/${_path}"
    fi

    if [[ ! -d "${_path}" ]]
    then
      _exit_1 printf "Not a directory: %s" "$(_color_primary "${_path}")"
    fi

    local _target_basename="${2:-}"
    if [[ -z "${_target_basename:-}" ]]
    then
      _target_basename="${_basename}"
    fi

    local _target_path=
    _target_path="$(_get_unique_path "${NB_DIR}/${_target_basename}")"

    cp -R "${_path}" "${_target_path}"  &&
      _temp cache clear                 &&
      _notebooks_add_new                &&
      printf "Imported notebook: %s\\n" \
        "$(_color_primary "$(basename "${_target_path}")")"
  }

  # _notebooks_init()
  #
  # Usage:
  #   _notebooks_init [<path> [<remote_url>]]
  _notebooks_init() {
    local _path=
    local _remote_url=

    for __arg in "${@:-}"
    do
      if _string_is_url "${__arg}" && [[ -z "${_remote_url:-}" ]]
      then
        _remote_url="${__arg}"
      elif [[ -z "${_path:-}" ]]
      then
        _path="${__arg}"
      fi
    done

    if [[ -n "${_remote_url:-}" ]] && [[ -z "${_path}" ]]
    then
      _exit_1 printf \
        "Must specify a <path> when cloning from <remote-url>.\\n%s\\n" \
        "$(_help "notebooks")"
    fi

    if [[ -n "${_path:-}" ]]
    then
      if [[ ! "${_path}" =~ ^/ ]]
      then
        _path="${_CURRENT_WORKING_DIR}/${_path}"
      fi

      if [[ ! -e "${_path}" ]]
      then
        mkdir -p "${_path}"
      elif [[ -f "${_path}" ]]
      then
        _exit_1 printf "File already exists: %s\\n" "$(_color_primary "${_path}")"
      fi
    else
      _path="${_CURRENT_WORKING_DIR}"
    fi

    if [[ -d "${_path}"       ]] &&
       [[ -d "${_path}/.git"  ]]
    then
      local _error_message=
      if [[ -f "${_path}/.index" ]]
      then
        _error_message="Notebook exists: $(_color_primary "${_path}")"
      else
        _error_message="Git repository exists: $(_color_primary "${_path}")"
      fi

      _exit_1 printf "%s\\n" "${_error_message}"
    fi

    if [[ "${_path}" != "${_CURRENT_WORKING_DIR}" ]] &&
       [[ -n "${_remote_url:-}"                   ]]
    then
      git clone "${_remote_url}" "${_path}"
    else
        git -C "${_path}" init 1>/dev/null  &&
        touch "${_path}/.index"             &&
        _git checkpoint "${_path}" "[nb] Initialize"
    fi

    printf "Initialized local notebook: %s\\n" "$(_color_primary "${_path}")"
  }

  # _notebooks_list()
  #
  # Usage:
  #   _notebooks_list [<name>] [--archived] [--local] [--global] [--names]
  #                   [--paths] [--unarchived]
  _notebooks_list() {
    local _counter=0
    local _query_names=()
    local _notebook_color_enabled="${_COLOR_ENABLED}"
    local _notebook_names=()
    local _notebook_paths=()
    local _only_archived=0
    local _only_global=0
    local _only_local=0
    local _only_names=0
    local _only_paths=0
    local _only_unarchived=0

    for __arg in "${@:-}"
    do
      case "${__arg}" in
        --archived)
          _only_archived=1
          ;;
        --local)
          _only_local=1
          ;;
        --global)
          _only_global=1
          ;;
        --name*)
          _only_names=1
          ;;
        --no*color)
          _notebook_color_enabled=0
          ;;
        --path*)
          _only_paths=1
          ;;
        --unarchived)
          _only_unarchived=1
          ;;
        *)
          _query_names+=("${__arg}")
          ;;
      esac
    done

    if [[ -z "${NB_DIR}" ]] || [[ ! -e "${NB_DIR}" ]]
    then
      exit 1
    fi

    _current_notebook_name="$(_notebooks current --name)"

    if [[ -z "${_query_names[*]:-}" ]]
    then
      _notebook_names=($(ls -1 "${NB_DIR}"))

      _notebooks_add_new "${_notebook_names[@]:-}"

      set +f
      _notebook_paths=($(ls -1 -d "${NB_DIR}"/*))
      set -f

      if [[ -n "${_LOCAL_NOTEBOOK_PATH:-}" ]]
      then
        _notebook_names=("local" "${_notebook_names[@]:-}")
        _notebook_paths=("${_LOCAL_NOTEBOOK_PATH}" "${_notebook_paths[@]:-}")
      fi
    else
      _notebooks_add_new

      for __name in "${_query_names[@]}"
      do
        _notebook_names+=("${__name}")

        if [[ "${__name}" == "local" ]]
        then
          _notebook_paths+=("${_LOCAL_NOTEBOOK_PATH}")
        else
          _notebook_paths+=("${NB_DIR}/${__name}")
        fi
      done
    fi

    for ((i=0; i < "${#_notebook_names[@]}"; i++))
    do
      if [[ -e "${_notebook_paths[i]}/.git" ]]
      then # it's a git repository.
        local _notebook_line=
        local _origin_url=

        if [[ "${i}" == "0" ]]                       &&
           [[ "${_notebook_names[i]}" == "local"  ]] &&
           [[ -n "${_LOCAL_NOTEBOOK_PATH:-}"      ]]
        then # local notebook as first notebook
          if ((_only_global))
          then
            continue
          else
            if ((_only_paths))
            then
              _notebook_line="${_notebook_paths[i]}"
            elif ((_notebook_color_enabled))                  &&
                 [[ "${_current_notebook_name}" == "local" ]]
            then
              _notebook_line="$(_color_primary "local" --underline)"
            else
              _notebook_line="local"
            fi
          fi
        elif ((_only_local))
        then
          if [[ -z "${_LOCAL_NOTEBOOK_PATH:-}" ]]
          then
            return 1
          else
            continue
          fi
        else
          if ((_only_paths))
          then
            _notebook_line="${_notebook_paths[i]}"
          elif ! ((_notebook_color_enabled))
          then
            _notebook_line="${_notebook_names[i]}"
          else
            if [[ "${_current_notebook_name}" == "${_notebook_names[i]}" ]]
            then # it's the current repository.
              _notebook_line="$(_color_primary "${_notebook_names[i]}")"

              if [[ "${#_notebook_names[@]}" -gt 1 ]]
              then
                _notebook_line="$(
                  _color_primary "${_notebook_names[i]}" --underline
                )"
              fi
            else
              _notebook_line="${_notebook_names[i]}"
            fi
          fi
        fi

        if [[ -e "${_notebook_paths[i]}/.archived" ]]
        then
          if ((_only_unarchived))
          then
            continue
          elif ! ((_only_names)) && ! ((_only_paths))
          then
            _notebook_line="${_notebook_line} (archived)"
          fi
        elif ((_only_archived))
        then
          continue
        fi

        if ! ((_only_names)) && ! ((_only_paths))
        then
          _origin_url="$(
            git -C "${_notebook_paths[i]}" config --get remote.origin.url || echo ''
          )"

          if [[ -n "${_origin_url:-}" ]]
          then
            _notebook_line="${_notebook_line} (${_origin_url})"
          fi
        fi

        printf "%s\\n" "${_notebook_line}"
        _counter=$((_counter+1))
      fi
    done

    if ! ((_counter))
    then
      if [[ -n "${_query_names[*]:-}" ]]
      then
        _warn printf                  \
          "Notebook not found: %s\\n" \
          "$(_color_primary "${_query_names[*]}" | tr '\r\n' ' ')" &&
            return 1
      else
        return 1
      fi
    fi
  }

  # _notebooks_notebook()
  #
  # Usage:
  #   _notebooks_notebook [archive | open | peek | status | unarchive] <name>
  _notebooks_notebook() {
    local _subcommand="${1:-}"
    local _notebook_name="${2:-}"

    if [[ -n "${_notebook_name:-}" ]]
    then
      if [[ "${_notebook_name}" =~ :$ ]]
      then
        _notebook_name=$(printf "%s\\n" "${_notebook_name}" | cut -f 1 -d ":")
      fi

      local _notebook_path
      _notebook_path="$(_notebooks "${_notebook_name}" --path)"
    else
      _notebook_name="$(_notebooks current)"
      _notebook_path="$(_notebooks current --path)"
    fi

    if [[ -z "${_notebook_name:-}" ]]
    then
      _exit_1 printf "Unable to determine notebook name.\\n"
    fi

    if [[ -z "${_notebook_path:-}" ]]
    then
      _exit_1 printf "Unable to determine notebook path.\\n"
    fi

    if [[ -z "${_subcommand}" ]]
    then
      printf "%s\\n" "${_notebook_name}"
      return 0
    fi

    if [[ "${_subcommand:-}" =~ ^open$|^o$ ]]
    then
      if _command_exists "xdg-open"
      then
        xdg-open "${_notebook_path}"
      elif _command_exists "open"
      then
        open "${_notebook_path}"
      else
        _exit_1 printf \
          "%s doesn't know how to open directories on your system.\\n" \
          "$(_color_primary "${_ME}")"
      fi
    elif [[ "${_subcommand:-}" =~ ^peek$|^p$ ]]
    then
      if _command_exists "ranger"
      then
        ranger "${_notebook_path}"
      elif _command_exists "mc"
      then
        mc "${_notebook_path}"
      elif _command_exists "exa"
      then
        exa -lah --git "${_notebook_path}"
      else
        # gnu || bsd
        ls -lah --color=always "${_notebook_path}" 2>/dev/null ||
          ls -lah -G "${_notebook_path}"
      fi
    else
      local _dotfile_path="${_notebook_path}/.archived"

      if [[ "${_subcommand}" == "archive" ]]
      then
        if [[ ! -e "${_dotfile_path}" ]]
        then
          touch "${_dotfile_path}" &&
            _git checkpoint "${_notebook_path}" "[nb] Archived"
        fi

        printf "%s archived.\\n" "$(_color_primary "${_notebook_name}")"
      elif [[ "${_subcommand}" == "status" ]]
      then
        if [[ -e "${_dotfile_path}" ]]
        then
          printf "%s is archived.\\n" "$(_color_primary "${_notebook_name}")"
        else
          printf "%s is not archived.\\n" "$(_color_primary "${_notebook_name}")"
        fi
      elif [[ "${_subcommand}" == "unarchive" ]]
      then
        if [[ -e "${_dotfile_path}" ]]
        then
          rm "${_dotfile_path:?}" &&
            _git checkpoint "${_notebook_path}" "[nb] Unarchived"
        fi

        printf "%s unarchived.\\n" "$(_color_primary "${_notebook_name}")"
      fi
    fi
  }

  # _notebooks_rename()
  #
  # Usage:
  #   _notebooks_rename <old> <new>
  _notebooks_rename() {
    local _old="${1:-}"
    local _new="${2:-}"

    if [[ -z "${_old}" ]] || [[ -z "${_new}" ]]
    then
      _exit_1 _help notebooks
    elif [[ -n "${_LOCAL_NOTEBOOK_PATH:-}" ]]
    then
      if [[ "${_old}" == "local" ]] || [[ "${_new}" == "local" ]]
      then
        _exit_1 cat <<HEREDOC
"local" refers to the local notbook and can not be renamed.
HEREDOC
      fi
    elif [[ ! -d "${NB_DIR}/${_old}" ]]
    then
      _exit_1 printf "%s is not a valid notebook name.\\n" \
        "$(_color_primary "${_old}")"
    elif [[ -e "${NB_DIR}/${_new}" ]]
    then
      _exit_1 printf "A notebook named \"%s\" already exists.\\n" \
        "$(_color_primary "${_new}")"
    fi

    mv "${NB_DIR}/${_old}" "${NB_DIR}/${_new}"

    if [[ "$(cat "${NB_DIR}/.current")" == "${_old}" ]]
    then
      printf "%s\\n" "${_new}" > "${NB_DIR}/.current"
    fi

    _temp cache clear

    printf "%s is now named %s\\n"  \
      "$(_color_primary "${_old}")" \
      "$(_color_primary "${_new}")"
  }

  # _notebooks_select()
  #
  # Usage:
  #   _notebook_select <selector>
  _notebooks_select() {
    local _selector="${1:-}"

    if [[ -z "${_selector:-}" ]]
    then
      _exit_1 _help "notebooks"
    fi

    local _notebook_path=
    _notebook_path="$(
      _notebooks_show "${_selector:-}" --path 2>/dev/null || :
    )"

    if [[ -n "${_notebook_path:-}" ]]
    then
      NB_NOTEBOOK_PATH="${_notebook_path}"
      return 0
    else
      return 1
    fi
  }

  # _notebooks_show()
  #
  # Usage:
  #   _notebooks_show (<name> | <path> | <selector>) [--archived]
  #                   [--escaped | --name | --path | --filename [<filename>]]
  _notebooks_show() {
    local _filename=
    local _local_notebook_path="${_LOCAL_NOTEBOOK_PATH:-}"
    local _notebook_color_enabled=1
    local _notebook_path=
    local _print_escaped_name=0
    local _print_unique_filename=0
    local _only_name=0
    local _only_path=0
    local _selector=
    local _test_archived=0

    while ((${#}))
    do
      local __arg="${1:-}"
      local __val="${2:-}"

      case "${__arg}" in
        --archived)
          _test_archived=1
          ;;
        --escaped)
          _print_escaped_name=1
          ;;
        --name)
          _only_name=1
          ;;
        --no*color)
          _notebook_color_enabled=0
          ;;
        --path)
          _only_path=1
          ;;
        --filename|--basename)
          _print_unique_filename=1

          if _option_value_is_present "${__val:-}"
          then
            _filename="${__val}"
            shift
          fi
          ;;
        *)
          if [[ -z "${_selector}" ]]
          then
            _selector="${__arg}"
          fi
          ;;
      esac

      shift
    done

    if [[ -z "${_selector:-}"      ]]
    then
      _exit_1 _help "notebooks"
    elif [[ "${_selector:-}" =~ ^/  ]]
    then
      if [[ -d "${_selector}/.git"   ]] &&
         [[ -f "${_selector}/.index" ]]
      then
        if [[ -n "${_local_notebook_path:-}"              ]] &&
           [[ "${_selector}" == "${_local_notebook_path}" ]]
        then
          _notebook_path="${_local_notebook_path}"
        else
          _notebook_path="${_selector}"
        fi
      fi
    elif [[ -n "${_selector:-}" ]]
    then
      local _selector_notebook="${_selector%%:*}"

      if [[ -n "${_selector_notebook:-}" ]]
      then
        if [[ "${_selector_notebook:-}" == "local"  ]] &&
           [[ -n "${_local_notebook_path:-}"        ]]
        then
          _notebook_path="${_local_notebook_path}"
        elif [[ -d "${NB_DIR}/${_selector_notebook:-}/.git"    ]] &&
             [[ -f "${NB_DIR}/${_selector_notebook:-}/.index"  ]]
        then
          _notebook_path="${NB_DIR}/${_selector_notebook}"
        fi
      fi
    fi

    if [[ -z "${_notebook_path:-}" ]]
    then
      _warn \
        printf "Notebook not found: %s\\n" "$(_color_primary "${_selector}")"
      return 1
    fi

    if ((_only_path))
    then
      printf "%s\\n" "${_notebook_path}"
      return 0
    fi

    if ((_test_archived))
    then
      if [[ -d "${_notebook_path}/.archived" ]]
      then
        return 0
      else
        return 1
      fi
    fi

    if ((_print_unique_filename))
    then
      local _unique_file_name=
      _unique_file_name="$(_get_unique_basename "${_filename:-}")"

      printf "%s\\n" "${_unique_file_name:-}"
      return 0
    fi

    local _notebook_name=

    if [[ "${_notebook_path}" == "${_LOCAL_NOTEBOOK_PATH:-}" ]]
    then
      _notebook_name="local"
    else
      _notebook_name="$(basename "${_notebook_path}")"
    fi

    if ((_only_name))
    then
      printf "%s\\n" "${_notebook_name}"
      return 0
    fi

    local _escaped_name="${_notebook_name}"

    if [[ "${_escaped_name}" =~ \  ]]
    then
      _escaped_name="$(
        printf "%s\\n" "${_escaped_name}" | sed -e "s/ /\\\ /g"
      )"
    fi

    if ((_print_escaped_name))
    then
      printf "%s\\n" "${_escaped_name:-}"
      return 0
    fi

    if ((_notebook_color_enabled)) &&
       [[ "${_notebook_path}" == "${NB_NOTEBOOK_PATH}" ]]
    then
      _escaped_name="$(_color_primary "${_escaped_name}")"
    fi

    local _notebook_line="${_escaped_name}"

    if [[ -e "${_notebook_path}/.archived" ]]
    then
      _notebook_line="${_notebook_line} (archived)"
    fi

    local _remote_url=
    _remote_url="$(
      git -C "${_notebook_path}" config --get remote.origin.url || echo ''
    )"

    if [[ -n "${_remote_url:-}" ]]
    then
      _notebook_line="${_notebook_line} (${_remote_url})"
    fi

    printf "%s\\n" "${_notebook_line}"
  }

  # _notebooks_use()
  #
  # Usage:
  #   _notebooks_use <name>
  _notebooks_use() {
    local _name="${1:-}"

    if [[ -z "${_name}" ]]
    then
      _exit_1 _help notebooks
    fi

    if [[ -n "${_LOCAL_NOTEBOOK_PATH}" ]]
    then
      _exit_1 cat <<HEREDOC
Currently in a local notebook. To run a command in a global notebook,
add the notebook name before the command name, separated by a colon:
  ${_ME} <notebook>:<command> [options...]

Examples:
  ${_ME} home:list
  ${_ME} example:add --title "Example Title"
  ${_ME} example:search "sample query"
HEREDOC
    fi

    # Autocomplete can result in a trailing colon on the notebook name, so
    # remove it if present.
    if [[ "${_name}" =~ :$ ]]
    then
      _name=$(printf "%s\\n" "${_name}" | cut -f 1 -d ":")
    fi

    if [[ -d "${NB_DIR}/${_name}/.git" ]]
    then
      printf "%s\\n" "${_name}" > "${NB_DIR}/.current"

      _GLOBAL_NOTEBOOK_PATH="${NB_DIR}/${_name}"
      NB_NOTEBOOK_PATH="${_GLOBAL_NOTEBOOK_PATH}"

      printf "Now using: %s\\n" "$(_color_primary "${_name}")"
    else
      _exit_1 printf "Not found: %s\\n" "$(_color_primary "${_name}")"
    fi
  }

  _notebooks_add_new() {
    local _notebook_list=("${@:-}")

    if [[ -z "${1:-}" ]]
    then
      _notebook_list=($(ls -1 "${NB_DIR}"))
    fi

    for __notebook_name in "${_notebook_list[@]:-}"
    do
      if [[ -d "${NB_DIR}/${__notebook_name}"         ]] &&
         [[ ! -d "${NB_DIR}/${__notebook_name}/.git"  ]]
      then
          git -C "${NB_DIR}/${__notebook_name}" init 1>/dev/null  &&
          touch "${NB_DIR}/${__notebook_name}/.index"             &&
          _temp cache clear                                       &&
          _git checkpoint "${NB_DIR}/${__notebook_name}" "[nb] Initialize"
      fi
    done
  }

  local _subcommand="${1:-}"
  local _name="${2:-}"

  if ! _contains "${_subcommand:-}" "current" "list" "show"
  then
    _notebooks_add_new
  fi

  case "${_subcommand}" in
    a|add|create|new)
      _notebooks_add "${_name:-}" "${3:-}"
      ;;
    export)
      _notebooks_export "${2:-}" "${3:-}"
      ;;
    import)
      _notebooks_import "${2:-}" "${3:-}"
      ;;
    init)
      _notebooks_init "${_name:-}" "${3:-}"
      ;;
    current)
      shift
      _notebooks_current "${@:-}"
      ;;
    d|delete)
      _notebooks_delete "${_name:-}" "${3:-}"
      ;;
    list)
      shift
      _notebooks_list "${@:-}"
      ;;
    rename)
      _notebooks_rename "${_name:-}" "${3:-}"
      ;;
    select)
      _notebooks_select "${2:-}"
      ;;
    s|show)
      shift
      _notebooks_show "${@:-}"
      ;;
    u|use)
      _notebooks_use "${_name:-}"
      ;;
    archive|open|o|peek|p|status|unarchive)
      _notebooks_notebook "${_subcommand}" "${_name:-}"
      ;;
    *)
      _notebooks_list "${@}"
      ;;
  esac
}
_alias_subcommand "notebooks" "n"
_alias_subcommand "notebooks" "nb"
_alias_subcommand "notebooks" "nbs"
_alias_subcommand "notebooks" "notebook"
_alias_subcommand "notebooks" "ns"

# open ################################################################### open

describe "open" <<HEREDOC
Usage:
  ${_ME} open (<id> | <filename> | <path> | <title> | <notebook>)

Description:
  Open a note or notebook. When the note is a bookmark, open the bookmarked
  page in your system's primary web browser. When the note is in a text format
  or any other file type, \`open\` is the equivalent of \`edit\`. \`open\`
  with a notebook opens the notebook folder in the system's file browser.

Examples:
  ${_ME} open 3
  ${_ME} open example.bookmark.md
  ${_ME} 3 open
  ${_ME} example:open 12
  ${_ME} open example:12
  ${_ME} example:12 open
  ${_ME} o 3
  ${_ME} 3 o
  ${_ME} o example:12
  ${_ME} example:12 o

See also:
  ${_ME} help bookmark
  ${_ME} help edit

Shortcut Alias: \`o\`
HEREDOC
_open() {
  if [[ -z "${1:-}" ]]
  then
    _exit_1 _help "open"
  fi

  _bookmark open "${@}"
}
_alias_subcommand "open" "o"

# peek ################################################################### peek

describe "peek" <<HEREDOC
Usage:
  ${_ME} peek (<id> | <filename> | <path> | <title> | <notebook>)

Description:
  View a note or notebook in the terminal. When the note is a bookmark, view
  the bookmarked page in your terminal web browser. When the note is in a text
  format or any other file type, \`peek\` is the equivalent of \`show\`. When
  used with a notebook, \`peek\` opens the notebook folder first tool found in
  the following list: \`ranger\` [1], \`mc\` [2], \`exa\` [3], or \`ls\`.

    1. https://ranger.github.io/
    2. https://en.wikipedia.org/wiki/Midnight_Commander
    3. https://github.com/ogham/exa

Examples:
  ${_ME} peek 3
  ${_ME} peek example.bookmark.md
  ${_ME} 3 peek
  ${_ME} example:peek 12
  ${_ME} peek example:12
  ${_ME} example:12 peek
  ${_ME} p 3
  ${_ME} 3 p
  ${_ME} p example:12
  ${_ME} example:12 p

See also:
  ${_ME} help bookmark
  ${_ME} help show

Alias: \`preview\`
Shortcut Alias: \`p\`
HEREDOC
_peek() {
  if [[ -z "${1:-}" ]]
  then
    _exit_1 _help "peek"
  fi

  _bookmark peek "${@}"
}
_alias_subcommand "peek" "p"
_alias_subcommand "peek" "preview"

# plugins ############################################################# plugins

describe "plugins" <<HEREDOC
Usage:
  ${_ME} plugins [<name>] [--paths]
  ${_ME} plugins install [<path> | <url>] [--force]
  ${_ME} plugins uninstall <name> [--force]

Options:
  --force  Skip the confirmation prompt.
  --paths  Print the full path to each plugin.

Subcommands:
  (default)  List plugins.
  install    Install a plugin from a <path> or <url>.
  uninstall  Uninstall the specified plugin.

Description:
  Manage plugins and themes.

Plugin Extensions:
  .nb-theme   Plugins defining color themes.
  .nb-plugin  Plugins defining new subcommands and functionality.
HEREDOC
_plugins() {
  local _basename=
  local _force=0
  local _name=
  local _path=
  local _print_paths=0
  local _subcommand=
  local _url=

  for __arg in "${@:-}"
  do
    case "${__arg}" in
      --force)
        _force=1
        ;;
      --path*)
        _print_paths=1
        ;;
      *)
        if [[ "${__arg}" =~ ^install$|^uninstall$ ]]
        then
          _subcommand="${__arg}"
        elif [[ "${_subcommand}" == "install" ]]
        then
          if [[ "${__arg:-}" =~ ^http|^file\: ]]
          then
            _url="${__arg}"
          else
            _path="${__arg}"
          fi
        else
          _name="${__arg}"
        fi
        ;;
    esac
  done

  case "${_subcommand}" in
    install)
      [[ ! -e "${NB_DIR}/.plugins" ]] && mkdir "${NB_DIR}/.plugins"

      if [[ -n "${_path:-}" ]]
      then
        if ! [[ -f "${_path:-}"     ]] &&
           ! [[ "${_path}" =~ nb\-  ]]
        then
          _exit_1 printf "Not a valid \`nb\` plugin: %s\\n" "${_path}"
        fi

        _basename="$(basename -- "${_path}")"

        if [[ -f "${NB_DIR}/.plugins/${_basename}" ]]
        then
          _warn printf "Plugin already installed: %s\\n" "${_basename}"

          if ! ((_force))
          then
            while true
            do
              IFS='' read -r -e -d $'\n' -p "\
$(_color_primary "Reinstall?") $(_color_brackets "y/N") " __yn

              case ${__yn} in
                [Yy]*)
                  break
                  ;;
                *)
                  printf "Exiting...\\n"
                  exit 0
                  ;;
              esac
            done
          fi
        fi

        if cp "${_path}" "${NB_DIR}/.plugins/${_basename}"
        then
          printf "Plugin installed:\\n%s\\n" \
            "$(_color_primary "${NB_DIR}/.plugins/${_basename}")"
        else
          _exit_1 printf "Unable to install plugin: %s\\n" "${_path}"
        fi
      else
        _basename="$(basename -- "${_url}" | tr -d '[:space:]')"

        if [[ "${_url:-}" =~ https\:\/\/github.com ]]
        then
          _url="$(
            printf "%s\\n" "${_url}"  \
              | sed                   \
                -e 's/\/blob//g'      \
                -e 's/https:\/\/github.com/https:\/\/raw.githubusercontent.com/g'
          )"
        fi

        if _download_from "${_url}" "${NB_DIR}/.plugins/${_basename}"
        then
          printf "Plugin installed:\\n%s\\n" \
            "$(_color_primary "${NB_DIR}/.plugins/${_basename}")"
        else
          _exit_1 printf "Unable to download and install: %s\\n"  "${_url}"
        fi
      fi
      ;;
    uninstall)
      local _plugin_path

      if ! _plugin_path="$(_plugins ${_name} --paths 2>/dev/null)"  ||
         [[ -z "${_plugin_path:-}" ]]                               ||
         ! [[ -f "${_plugin_path}" ]]
      then
        _exit_1 printf "Plugin not found: %s\\n" "$(_color_primary "${_name}")"
      fi

      if ! ((_force))
      then
        cat <<HEREDOC
Uninstalling plugin. Warning: This will permanently delete the following file:
$(_color_primary "${_plugin_path}")
HEREDOC
        while true
        do
          IFS='' read -r -e -d $'\n' -p "\
$(_color_primary "Proceed?") $(_color_brackets "y/N") " __yn

          case ${__yn} in
            [Yy]*)
              break
              ;;
            *)
              printf "Exiting...\\n"
              exit 0
              ;;
          esac
        done
      fi

      if [[ -f "${_plugin_path}" ]] && rm "${_plugin_path:?}"
      then
        printf "Plugin successfully uninstalled:\\n%s\\n" \
          "$(_color_primary "${_plugin_path}")"
      else
        _exit_1 printf "Unable to uninstall plugin:\\n%s\\n" "${_plugin_path}"
      fi
      ;;
    *)
      local _query="*nb-*"

      if [[ -n "${_name}" ]]
      then
        _query="${_name}"
      fi

      if [[ ! -e "${NB_DIR}/.plugins" ]]
      then
        (return 1)
      elif ((_print_paths))
      then
        # `grep` for non-0 `find`. More info: https://serverfault.com/a/855083
        find "${NB_DIR}"/.plugins \
          -type f                 \
          -name "${_query}"       \
          | grep .
      else
        find "${NB_DIR}"/.plugins \
          -type f                 \
          -name "${_query}"       \
          -exec basename {} \;    \
          | grep .
      fi || if [[ -n "${_name:-}" ]]
            then
              _exit_1 printf "No matching plugins found: %s\\n" "${_name}"
            else
              _exit_1 printf "No plugins found.\\n"
            fi
      ;;
  esac
}
_alias_subcommand "plugins" "plugin"

# remote ############################################################### remote

describe "remote" <<HEREDOC
Usage:
  ${_ME} remote
  ${_ME} remote remove
  ${_ME} remote set <url> [-f | --force]

Subcommands:
  (default)     Print the remote URL for the notebook.
  remove        Remove the remote URL from the notebook.
  set           Set the remote URL for the notebook.

Options:
  -f, --force   Skip the confirmation prompt.

Description:
  Get, set, and remove the remote repository URL for the current notebook.

Examples:
  ${_ME} remote set https://github.com/example/example.git
  ${_ME} remote remove
HEREDOC
_remote() {
  local _existing_url=
  local _force=0
  local _remote_exists=0
  local _subcommand=
  local _url=

  for __arg in "${@:-}"
  do
    case "${__arg}" in
      -f|--force)
        _force=1
        ;;
      *)
        if [[ -z "${_subcommand}" ]]
        then
          _subcommand="${__arg}"
        elif [[ "${_subcommand}" =~ ^set$|^add$ ]] &&
             [[ -z "${_url}" ]]
        then
          _url="${__arg}"
        fi
        ;;
    esac
  done

  local _notebook_path
  _notebook_path="$(_notebooks current --path)"

  if [[ "$(git -C "${_notebook_path}" remote get-url origin 2>/dev/null)" ]]
  then
    _remote_exists=1
    _existing_url="$(git -C "${_notebook_path}" remote get-url origin)"
  fi

  case "${_subcommand}" in
    remove|rm|unset)
      if ((_remote_exists))
      then
        if ! ((_force))
        then
          while true
          do
            printf "Removing remote: %s\\n" "$(_color_primary "${_existing_url}")"

            IFS='' read -r -e -d $'\n' -p "\
$(_color_primary "Proceed?") $(_color_brackets "y/N") " __yn

            case ${__yn} in
              [Yy]*)
                break
                ;;
              *)
                printf "Exiting...\\n"
                exit 0
                ;;
            esac
          done
        fi

        git -C "${_notebook_path}" remote rm origin &&
          printf "Removed remote: %s\\n"            \
            "$(_color_primary "${_existing_url}")"  &&
              return 0
      else
        _exit_1 printf "No remote configured.\\n"
      fi
      ;;
    set|add)
      if [[ -z "${_url}" ]]
      then
        _exit_1 _help "remote"
      fi

      if ((_remote_exists)) && [[ "${_existing_url:-}" == "${_url:-}" ]]
      then
        _exit_1 printf "Remote already set to: %s\\n" \
          "$(_color_primary "${_url}")"
      fi

      if ! ((_force))
      then
        while true
        do
          if ((_remote_exists))
          then
            printf "Updating remote for: %s\\n" \
              "$(_color_primary "$(_notebooks current --name)")"

            _print_line "Updating remote for: $(_notebooks current --name)"

            printf "From: %s\\n" "${_existing_url}"
            printf "To:   %s\\n" "$(_color_primary "${_url}")"
          else
            printf "Adding remote to: %s\\n" \
              "$(_color_primary "$(_notebooks current --name)")"

            _print_line "Adding remote to: $(_notebooks current --name)"

            printf "URL: %s\\n" "$(_color_primary "${_url}")"
          fi

          _print_line "--------------"

          IFS='' read -r -e -d $'\n' -p "\
$(_color_primary "Proceed?") $(_color_brackets "y/N") " __yn

          case ${__yn} in
            [Yy]*)
              break
              ;;
            *)
              printf "Exiting...\\n"
              exit 0
              ;;
          esac
        done
      fi

      local _subcommand="add"

      if ((_remote_exists))
      then
        _subcommand="set-url"
      fi

      git -C "${_notebook_path}" remote "${_subcommand}" origin "${_url}" &&
        printf "Remote set to: %s\\n" "$(_color_primary "${_url}")"        &&
          return 0
      ;;
    *)
      if ((_remote_exists))
      then
        git -C "${_notebook_path}" remote get-url origin && return 0
      else
        _warn printf "No remote configured.\\n"
        return 1
      fi
      ;;
  esac
}

# rename ############################################################### rename

describe "rename" <<HEREDOC
Usage:
  ${_ME} rename (<id> | <filename> | <path> | <title>) [-f | --force]
            (<name> | --reset | --to-bookmark | --to-note)

Options:
  -f, --force     Skip the confirmation prompt.
  --reset         Reset the filename to the last modified timestamp.
  --to-bookmark   Preserve the existing filename and replace the extension
                  with ".bookmark.md" to convert the note to a bookmark.
  --to-note       Preserve the existing filename and replace the bookmark's
                  ".bookmark.md" extension with ".md" to convert the bookmark
                  to a Markdown note.

Description:
  Rename a note. Set the filename to <name> for the specified note file. When
  file extension is omitted, the existing extension will be used.

Examples:
  # Rename "example.md" to "example.org"
  ${_ME} rename example.md example.org

  # Rename note 3 ("example.md") to "New Name.md"
  ${_ME} rename 3 "New Name"

  # Rename "example.bookmark.md" to "New Name.bookmark.md"
  ${_ME} rename example.bookmark.md "New Name"

  # Rename note 3 ("example.md") to bookmark named "example.bookmark.md"
  ${_ME} rename 3 --to-bookmark

  # Rename note 12 in the "example" notebook to "sample.md"
  ${_ME} example:rename 3 "sample.md"
HEREDOC
_rename() {
  local _force=0
  local _reset=0
  local _selector=
  local _target_basename=
  local _target_name=
  local _to_target_type=

  while ((${#}))
  do
    local __arg="${1:-}"
    local __val="${2:-}"

    case "${__arg}" in
      -f|--force)
        _force=1
        ;;
      --reset)
        _reset=1
        ;;
      --to*bookmark)
        _to_target_type="bookmark"
        ;;
      --to*note)
        _to_target_type="note"
        ;;
      *)
        if [[ -z "${_selector}"       ]]
        then
          _selector="${__arg}"
        elif [[ -z "${_target_name}"  ]]
        then
          _target_name="${__arg}"
        fi
        ;;
    esac

    shift
  done

  if [[ -z "${_selector}" ]]
  then
    _exit_1 _help "rename"
  fi

  local _source_basename
  _source_basename="$(_show "${_selector}" --filename)"

  local _notebook_path
  _notebook_path="$(_notebooks current --path)"

  local _source_path="${_notebook_path}/${_source_basename}"

  if [[ -z "${_source_basename}" ]]
  then
    _exit_1 _help "rename"
  fi

  if ((_reset))
  then
    local _file_type="${NB_DEFAULT_EXTENSION}"

    _target_basename="$(date -r "${_source_path}" "+%Y%m%d%H%M%S").${_file_type}"
  elif [[ -n "${_target_name:-}"    ]] ||
       [[ -n "${_to_target_type:-}" ]]
  then
    local _source_file_name="${_source_basename%%.*}"
    local _source_file_type="${_source_basename#*.}"
    if [[ -z "${_source_file_type:-}"                     ]] ||
       [[ "${_source_file_type}" == "${_source_basename}" ]]
    then
      _source_file_type=
    fi

    local _target_file_name="${_target_name%%.*}"
    local _target_file_type="${_target_name#*.}"
    if [[ -z "${_target_file_type:-}"                 ]] ||
       [[ "${_target_file_type}" == "${_target_name}" ]]
    then # no file extension specified in _target_name
      if [[ -n "${_source_file_type}"                 ]]
      then
        _target_file_type="${_source_file_type}"
      else
        _target_file_type=
      fi
    fi

    if [[ -n "${_to_target_type:-}" ]]
    then
      if [[ "${_to_target_type}" == "bookmark"  ]]
      then
        _target_file_type="bookmark.md"
      elif [[ "${_to_target_type}" == "note"    ]]
      then
        _target_file_type="md"
      else
        _exit_1 _help "rename"
      fi
    fi

    if [[ -z "${_target_file_name:-}" ]]
    then
      _target_file_name="${_source_file_name}"
    fi

    if [[ -n "${_target_file_type:-}" ]]
    then
      _target_basename="${_target_file_name}.${_target_file_type}"
    else
      _target_basename="${_target_name}"
    fi

    if [[ -e "${_notebook_path}/${_target_basename}" ]]
    then
      _exit_1 printf "File already exists: %s\\n" \
        "$(_color_primary "${_target_basename}")"
    fi
  else
    _exit_1 _help "rename"
  fi

  local _source_info
  _source_info="$(_show "${_source_basename}" --info-line)"

  if ! ((_force))
  then
    printf "Renaming: %s\\n" "${_source_info}"
    printf "To:        %s\\n" "$(_color_primary "${_target_basename}")"

    while true
    do
      IFS='' read -r -e -d $'\n' -p "\
$(_color_primary "Proceed?")  $(_color_brackets "y/N") " __yn

      case ${__yn} in
        [Yy]*)
          break
          ;;
        *)
          printf "Exiting...\\n"
          exit 0
          ;;
      esac
    done
  fi

  mv "${_source_path}" "${_notebook_path}/${_target_basename}"  &&
    _index update "${_source_basename}" "${_target_basename}"   &&
      _git checkpoint "${_notebook_path}"     \
        "[nb] Rename: ${_source_basename} to ${_target_basename}"

  printf "%s renamed to %s\\n"                \
    "${_source_info}"                         \
    "$(_color_primary "${_target_basename}")"
}

# run ##################################################################### run

describe "run" <<HEREDOC
Usage:
  ${_ME} run <command> [<arguments>...]

Description:
  Run shell commands within the current notebook directory.

Examples:
  ${_ME} run ls -la
  ${_ME} run find . -name 'example*'
  ${_ME} run rg example
HEREDOC
_run() {
  local _notebook_path
  _notebook_path="$(_notebooks current --path)"

  cd "${_notebook_path}"  || _exit_1 printf "_run() \`cd\` failed.\\n"
  [[ -n "${*}" ]]         || _exit_1 printf "Command required.\\n"

  ("${@}")
}
_alias_subcommand "run" "r"

# search ############################################################### search

describe "search" <<HEREDOC
Usage:
  ${_ME} search <query> [-a | --all] [-t <type> | --type <type> | --<type>]
                    [-l | --list] [--path]

Options:
  -a, --all                     Search all unarchived notebooks.
  -l, --list                    Print the id, filename, and title listing for
                                each matching file, without the excerpt.
  --path                        Print the full path for each matching file.
  -t, --type <type>, --<type>   Search items of <type>. <type> can be a file
                                extension or one of the following types:
                                note, bookmark, document, archive, image,
                                video, audio, folder, text
Description:
  Search notes. Uses the first available tool in the following list:
    1. \`rg\`    https://github.com/BurntSushi/ripgrep
    2. \`ag\`    https://github.com/ggreer/the_silver_searcher
    3. \`ack\`   https://beyondgrep.com/
    4. \`grep\`  https://en.wikipedia.org/wiki/Grep

Examples:
  # search current notebook for "example query"
  ${_ME} search "example query"

  # search the notebook "example" for "example query"
  ${_ME} example:search "example query"

  # search all notebooks for "example query" and list matching items
  ${_ME} search "example query" --all --list

  # search notes for "Example" OR "Sample"
  ${_ME} search "Example|Sample"

  # search with a regular expression
  ${_ME} search "\d\d\d-\d\d\d\d"

  # search the current notebook for "example query"
  ${_ME} q "example query"

  # search the notebook named "example" for "example query"
  ${_ME} example:q "example query"

  # search all notebooks for "example query" and list matching items
  ${_ME} q -la "example query"

Shortcut Alias: \`q\`
HEREDOC
_search() {
  local _all=0
  local _last_path=
  local _only_list=0
  local _max_columns="2000"
  local _print_paths=0
  local _query=
  local _search_args=()
  local _search_color_enabled="${_COLOR_ENABLED}"
  local _sort=0
  local _type=
  local _use_grep=0

  while ((${#}))
  do
    local __arg="${1:-}"
    local __val="${2:-}"

    case "${__arg}" in
      -a|--all)
        _all=1
        ;;
      -l|--list)
        _only_list=1
        ;;
      --no*color)
        _search_color_enabled=0
        ;;
      --path*)
        _print_paths=1
        ;;
      -s|--sort)
        _sort=1
        ;;
      --type)
        _type="$(_option_get_value "${__arg}" "${__val:-}")"
        shift
        ;;
      --use*grep)
        # The `--use-grep` option bypasses other search tools. Intended to be
        # used primarily for testing.
        _use_grep=1
        ;;
      *)
        if [[ "${__arg:-}" =~ ^-- ]]
        then
          _type="${__arg:2}"
        elif [[ -z "${_query}"    ]]
        then
          _query="${__arg}"
        fi
        ;;
    esac

    shift
  done

  if [[ -z "${_query:-}" ]]
  then
    _exit_1 _help "search"
  else
    local _target_notebook_paths=()

    if ((_all))
    then
      _target_notebook_paths=($(_notebooks --paths --unarchived))
    else
      _target_notebook_paths=($(_notebooks current --path))
    fi

    for __notebook_path in "${_target_notebook_paths[@]:-}"
    do
      local _max_id
      _max_id="$(NB_NOTEBOOK_PATH="${__notebook_path}" _index get_max_id)"

      if _command_exists "rg" && ! ((_use_grep))
      then
        if ((_search_color_enabled))
        then
          _search_args+=("--color" "always")
        else
          _search_args+=("--color" "never")
        fi

        if ((_sort))
        then
          _search_args+=("--sort-files")
        fi

        rg                                \
          --hidden                        \
          --iglob '!.git'                 \
          --ignore-case                   \
          --no-heading                    \
          --line-number                   \
          --max-columns "${_max_columns}" \
          --max-columns-preview           \
          --max-depth 1                   \
          "${_search_args[@]}"            \
          "${_query}"                     \
          "${__notebook_path}"            \
            || return 0 # Don't fail out within a single scope.
      elif _command_exists "ag" && ! ((_use_grep))
      then
        if ((_search_color_enabled))
        then
          _search_args+=("--color")
          _search_args+=("--color-line-number"  "32")
          _search_args+=("--color-match"        "1;35")
        else
          _search_args+=("--nocolor")
        fi

        # TODO: ag doesn't support sorting directly.
        # if ((_sort))
        # then
        #   _search_args+=("--workers" "1")
        # fi

        ag                                  \
          --hidden                          \
          --ignore-case                     \
          --noheading                       \
          --norecurse                       \
          "${_search_args[@]}"              \
          "${_query}" "${__notebook_path}"  \
            || return 0 # Don't fail out within a single scope.
      elif _command_exists "ack" && ! ((_use_grep))
      then # ack is available.
        if ((_search_color_enabled))
        then
          _search_args+=("--color")
          _search_args+=("--color-lineno=bold green")
          _search_args+=("--color-match=blue on_black")
        else
          _search_args+=("--nocolor")
        fi

        if ((_sort))
        then
          _search_args+=("--sort-files")
        fi

        ack "${_query}" "${__notebook_path}"  \
          --ignore-case                       \
          --noheading                         \
          --no-recurse                        \
          "${_search_args[@]}"                \
            || return 0 # Don't fail out within a single scope.
      else # fall back to POSIX grep.
        if ((_search_color_enabled))
        then
          _search_args+=("--color=always")
        else
          _search_args+=("--color=never")
        fi

        # Add /dev/null so file path is printed even if there is only one
        # matching file. Reference: https://stackoverflow.com/a/15432718
        set +f
        {
          # Print .index with null byte delimeter so it's included in
          # the list of files to search.
          printf "%s/.index\0" "${__notebook_path}"
          find "${__notebook_path}"/* \
            \( -name . -o -prune \)   \
            -type f                   \
            -print0
        } | xargs -0 grep           \
              -E                    \
              --ignore-case         \
              "${_search_args[@]}"  \
              --text                \
              -n                    \
              "${_query}"           \
               /dev/null            \
                || return 0 # Don't fail out within a single scope.
        set -f
      fi | while read -r _line
      do
        local _content_line_with_hit=
        local _file_info=
        local _file_info_color=
        local _filename=
        local _filename_color=
        local _filename_hit=0
        local _id=
        local _path=
        local _skip=0
        local _title=

        # Use `sed` to remove color and 'matches' suffix from output.
        # https://unix.stackexchange.com/a/140255
        _path="$(
          printf "%s\\n" "${_line}"                         \
            | cut -d: -f 1                                  \
            | sed "s,$(printf '\033')\\[[0-9;]*[a-zA-Z],,g" \
            | sed -e 's/ matches//g'
        )"

        if [[ "${_last_path}" != "${_path}" ]]
        then
          if [[ -n "${_type:-}" ]]
          then
            if [[ "${_path}" =~ ${_type}$ ]]
            then
              _skip=0
            elif {
                [[ "${_type}" =~ ^bookmark$|^bookmarks$                 ]] &&
                _file_is_bookmark "${_path}"
              } || {
                [[ "${_type}" =~ ^note$|^notes$|^text$                  ]] &&
                _file_is_text "${_path}"
              } || {
                [[ "${_type}" =~ ^folder$|^folders$                     ]] &&
                [[ "$(dirname "${_path}")" != "${__notebook_path}"      ]]
              } || {
                [[ "${_type}" =~ ^archive$                              ]] &&
                _file_is_archive "${_path}"
              } || {
                [[ "${_type}" =~ ^audio$|^music$                        ]] &&
                _file_is_audio "${_path}"
              } || {
                [[ "${_type}" =~ ^document$|^documents$|^doc$|^docs$    ]] &&
                _file_is_document "${_path}"
              } || {
                [[ "${_type}" =~ ^encrypted$                            ]] &&
                _file_is_encrypted "${_path}"
              } || {
                [[ "${_type}" =~ ^image$|^images$|^picture$|^pictures$  ]] &&
                _file_is_image "${_path}"
              } || {
                [[ "${_type}" =~ ^video$|^videos$                       ]] &&
                _file_is_video "${_path}"
              }
            then
              _skip=0
            else
              _skip=1
            fi
          fi
        fi

        if [[ -z "${_line}" ]]
        then
          # Print nothing, skipping the blank line in `ag` output with
          # multiple matches.
          :
        elif ((_print_paths))
        then
          if [[ "${_last_path}" != "${_path}" ]]
          then # match is in a different file than the last match
            printf "%s\\n" "${_path}"
          fi
        else
          if ((_skip))
          then
            continue
          fi

          _filename="$(basename "${_path}")"
          _filename_color="${_filename}"

          # Use ".index" to match filenames.
          if [[ -n "${_filename:-}" ]] && [[ "${_filename:-}" == ".index" ]]
          then
            _filename_hit=1
            _filename_color="$(printf "%s\\n" "${_line}" | cut -d: -f 3-)"
            _filename="$(
              printf "%s\\n" "${_filename_color}" \
                | sed "s,$(printf '\033')\\[[0-9;]*[a-zA-Z],,g"
            )"
          fi

          local _filename_for_id="${_filename}"

          if [[ "${__notebook_path}/${_filename}" != "${_path}" ]] &&
             [[ ! "${_path:-}" =~ \.index$                      ]]
          then
            local _parent_dirname=
            _parent_dirname="$(dirname "${_path}")"

            _filename_for_id="$(basename "${_parent_dirname}")"

            local _relative_parent_dirname=
            _relative_parent_dirname="${_parent_dirname#${__notebook_path}/}"

            _filename="${_relative_parent_dirname}/${_filename}"
            _filename_color="${_relative_parent_dirname}/${_filename_color}"
          fi

          _id="$(
            NB_NOTEBOOK_PATH="${__notebook_path}" \
              _index get_id "${_filename_for_id}"
          )"

          if ((_filename_hit))
          then
            _title="$(_get_title "${__notebook_path}/${_filename_for_id}")"

            if [[ -n "${_title}" ]]
            then
              _file_info="${_filename} · ${_title}"
              _file_info_color="${_filename_color} · ${_title}"
            else
              _file_info="${_filename}"
              _file_info_color="${_filename_color}"
            fi
          else
            local _first_line=
            _title="$(_get_title "${_path}")"

            if [[ -n "${_title:-}" ]]
            then
              _file_info="${_title}"
              _file_info_color="${_title}"
            elif _first_line="$(_get_first_line "${_path}")"
            then
              _file_info="${_filename} · \"${_first_line}\""
              _file_info_color="${_filename_color} · \"${_first_line}\""
            else
              _file_info="${_filename}"
              _file_info_color="${_filename_color}"
            fi
          fi

          local _indicators=
          _indicators="$(
            NB_NOTEBOOK_PATH="${__notebook_path}" \
              _show "${_filename_for_id}" --indicators
          )"

          local _info_line=
          local _info_line_color=
          local _max_identifier=

          if ((_all)) ||
             [[ "$(_notebooks current --global --path)" != "${__notebook_path}" ]]
          then
            local _notebook_escaped_name=
            _notebook_escaped_name="$(
              _notebooks show "${__notebook_path}" --escaped --no-color
            )"

            _info_line="[${_notebook_escaped_name}:${_id}] "
            _info_line_color+="$(_color_brackets "${_notebook_escaped_name}:${_id}") "
            _max_identifier="[${_notebook_escaped_name}:${_max_id}] "
          else
            _info_line="[${_id}] "
            _info_line_color+="$(_color_brackets "${_id}") "
            _max_identifier="[${_max_id}] "
          fi

          # Use calculated number of spaces for nicer formatting.
          local _spaces=''
          local _spaces_length=0

          if ((_only_list))
          then
            _spaces_length=$(( ${#_max_identifier} - ${#_info_line} ))
          fi

          if ((_spaces_length))
          then
            printf -v _spaces '%*s' "${_spaces_length}" ""

            _info_line+="${_spaces}"
            _info_line_color+="${_spaces}"
          fi

          if [[ -n "${_indicators:-}" ]]
          then
            _info_line+="${_indicators}"
            _info_line_color+="${_indicators}"
          fi

          _info_line+="${_file_info}"
          _info_line_color+="${_file_info_color}"

          if [[ "${_last_path}" != "${_path}" ]] || ((_filename_hit))
          then # match is in a different file than the last match
            _wrap off
            printf "%s" "${_info_line_color}"
            _wrap on
            printf "\\n"
          fi

          if ((_only_list))
          then
            [[ -n "${_path}" ]] && _last_path="${_path}"
            continue
          fi

          if [[ "${_last_path}" != "${_path}" ]] || ((_filename_hit))
          then # match is in a different file than the last match
            _wrap off

            local _visible_line_length
            _visible_line_length="$(
              _get_visible_length "${_info_line:-}"
            )"

            _print_line "$(printf "%-${_visible_line_length}s" '.')"

            _wrap on
          fi

          if ((_filename_hit))
          then
            _content_line_with_hit="Filename Match: $(
              printf "%s\\n" "${_line}" | cut -d: -f 3-
            )"
          else
            _content_line_with_hit="$(
              printf "%s\\n" "${_line}" | cut -d: -f 2-
            )"
          fi

          # Truncate long lines.
          local _line_length=
          _line_length="$(
            printf "%s\\n" "${_content_line_with_hit}" | awk '{print length}'
          )"
          local _line_length_diff
          _line_length_diff=$((_line_length-_max_columns-100))

          if [[ "${_line_length_diff}" -gt 0 ]]
          then
            _content_line_with_hit="$(\
              printf "%s\\n" "${_content_line_with_hit}" \
                | awk '{ print substr($0, 1, 2000) }'
            )"
            _content_line_with_hit+="$(
              _color_primary "... [${_line_length_diff} characters omitted]"
            )"
          fi

          printf "%s\\n\\n" "${_content_line_with_hit}"
        fi

        [[ -n "${_path}" ]] && _last_path="${_path}"
      done
    done | if read -r _line
    then # output is present
      # Print the test line then `cat` the rest.
      printf "%s\\n" "${_line}"
      cat
      return 0
    else
      if ((_all))
      then
        printf "Not found in any notebook: %s\\n" \
          "$(_color_primary "${_query:-}")"
      else
        printf "Not found in %s: %s\\n"                       \
          "$(_color_primary "$(_notebooks current --name)")"  \
          "$(_color_primary "${_query:-}")"
      fi

      return 1
    fi
  fi
}
_alias_subcommand "search" "q"

# settings ########################################################### settings

describe "settings" <<HEREDOC
Usage:
  ${_ME} set [<name> [<value>] | <number> [<value>]]
  ${_ME} settings colors [<number> | themes]
  ${_ME} settings edit
  ${_ME} settings get   (<name> | <number>)
  ${_ME} settings list  [--long]
  ${_ME} settings set   (<name> | <number>) <value>
  ${_ME} settings show  (<name> | <number>)
  ${_ME} settings unset (<name> | <number>)

Subcommands:
  (default)  Open the settings prompt, to <name> or <number>, if present.
             When <value> is also present, assign <value> to the setting.
  colors     Print a table of available colors and their xterm color numbers.
             When <number> is provided, print the number in its color.
             \`settings colors themes\` prints a list of installed themes.
  edit       Open the \`${_ME}\` configuration file in \`\$EDITOR\`.
  get        Print the value of a setting.
  list       List information about available settings.
  set        Assign <value> to a setting.
  show       Print the help information and current value of a setting.
  unset      Unset a setting, returning it to the default value.

Description:
  Configure \`${_ME}\`. Use \`${_ME} settings set\` to customize a setting and
  \`${_ME} settings unset\` to restore the default for a setting.

  Use the \`${_ME} set\` alias to quickly assign values to settings:

    ${_ME} set color_theme blacklight
    ${_ME} set limit 40

Examples:
  ${_ME} settings
  ${_ME} set 5 "org"
  ${_ME} set color_primary 105
  ${_ME} set unset color_primary
  ${_ME} set color_secondary unset
  ${_ME} settings colors
  ${_ME} settings colors 105
  ${_ME} set limit 15

Alias: \`set\`
HEREDOC
_settings() {
  # Usage: _settings_colors [<number>] [themes]
  _settings_colors() {
    # Usage: _settings_colors_print <number>
    _settings_colors_print() {
      printf '\e[38;5;%dm %3d ' "${1:-}" "${1:-}"
    }

    if [[ -n "${1:-}" ]]
    then
      if [[ "${1}" == "themes" ]]
      then
        printf "%s\\n" "${_NB_COLOR_THEMES[@]}"
      else
        [[ "${1}" =~ [[:digit:]] ]] || _exit_1 \
          printf "Usage: %s settings colors [<number> | themes]\\n" "${_ME}"

        _settings_colors_print "${1:-}"
        printf "\\n"
      fi
    else
      for ((_i=0; _i < 256; _i++))
      do
        if ((_i)) && ! ((_i % 16))
        then
          printf "\\n"
        fi

        _settings_colors_print "${_i}"
      done

      printf "\\n"
    fi
  }

  # Usage: _settings_get_value (<number> | <name>)
  _settings_get_value() {
    local _setting_name=
    _setting_name="$(_settings_identify "${1:-}")" &&
      _env | grep "${_setting_name}" | cut -d= -f2
  }

  # Usage: _settings_identify [--id] (<number> | <name>)
  _settings_identify() {
    local _get_id=0
    local _id_or_name=

    if [[ -z "${1:-}" ]]
    then
      return 1
    elif [[ "${1}" == "--id" ]]
    then
      _get_id=1
      [[ -z "${2:-}" ]] && return 1
      _id_or_name="${2:-}"
    else
      _id_or_name="${1}"
    fi

    case "${_id_or_name}" in
      1|*auto*|*AUTO*|*sync*|*SYNC*)
        ((_get_id)) && printf "1" || printf "NB_AUTO_SYNC"
        return 0
        ;;
      2|*primary*|*PRIMARY*)
        ((_get_id)) && printf "2" || printf "NB_COLOR_PRIMARY"
        return 0
        ;;
      3|*secondary*|*SECONDARY*)
        ((_get_id)) && printf "3" || printf "NB_COLOR_SECONDARY"
        return 0
        ;;
      4|*color*theme*|*COLOR*THEME*|*scheme*|theme*)
        ((_get_id)) && printf "4" || printf "NB_COLOR_THEME"
        return 0
        ;;
      5|*ext*|*EXT*)
        ((_get_id)) && printf "5" || printf "NB_DEFAULT_EXTENSION"
        return 0
        ;;
      6|*editor*|*EDITOR*)
        ((_get_id)) && printf "6" || printf "EDITOR"
        return 0
        ;;
      7|*encrypt*|*ENCRYPT*)
        ((_get_id)) && printf "7" || printf "NB_ENCRYPTION_TOOL"
        return 0
        ;;
      8|*footer*|*FOOTER*)
        ((_get_id)) && printf "8" || printf "NB_FOOTER"
        return 0
        ;;
      9|*header*|*HEADER*)
        ((_get_id)) && printf "9" || printf "NB_HEADER"
        return 0
        ;;
      10|*limit*|*LIMIT*)
        ((_get_id)) && printf "10" || printf "NB_LIMIT"
        return 0
        ;;
      11|*dir*|*DIR*)
        ((_get_id)) && printf "11" || printf "NB_DIR"
        return 0
        ;;
      12|*syntax*|*SYNTAX*)
        ((_get_id)) && printf "12" || printf "NB_SYNTAX_THEME"
        return 0
        ;;
      *)
        _warn                                   \
          printf "Setting not found: %s\\n"     \
          "$(_color_primary "${_id_or_name}")"  &&
            return 1
        ;;
    esac
  }

  # Usage: _settings_prompt [<id> | <name>]
  _settings_prompt() {
    local _setting_name="${1:-}"

    local _columns
    _columns="$(tput cols)"

    local _padding
    _padding="$(_print_padding "-----------" "${_columns}")"

    printf "%s%s Settings\\n" "${_padding}" "$(_color_primary "${_ME}")"
    _print_line "$(printf "%-${_columns}s" '.')"

    if [[ -n "${_setting_name}" ]]
    then
      _setting_name="$(_settings_identify "${_setting_name}")"
    else
      cat <<HEREDOC
$(_settings list)

Enter the number or name of the setting to change, or $(_color_primary 'q') to quit.
HEREDOC

      while true
      do
        IFS='' read -r -e -d $'\n' -p "\
$(_color_primary "Number or Name: ")" __setting

        if [[ -n "${__setting}" ]]
        then
          case "${__setting}" in
            q|quit|exit)
              printf "Exiting...\\n"
              return 0
              ;;
            *)
              _setting_name="$(
                _settings_identify "${__setting}"
              )" && break
              ;;
          esac
        fi
      done
    fi

    printf "\\n"

    if [[ "${_setting_name}" == "NB_COLOR_PRIMARY"    ]] ||
       [[ "${_setting_name}" == "NB_COLOR_SECONDARY"  ]]
    then
      printf "%s\\n\\n" "$(_settings_colors)"
    fi

    _settings show "${_setting_name}"

    local _hi_unset= && _hi_unset="$(_color_primary 'unset')"
    local _hi_q=     && _hi_q="$(_color_primary 'q')"

    cat <<HEREDOC

Enter a new value, ${_hi_unset} to set to the default value, or ${_hi_q} to quit.
HEREDOC

    while true
    do
      IFS='' read -r -e -d $'\n' -p "$(_color_primary "Value: ")" __value

      if [[ -n "${__value}" ]]
      then
        case "${__value}" in
          q|quit|exit)
            printf "Exiting...\\n"
            return 0
            ;;
          unset|default|reset)
            _settings_unset "${_setting_name}" || continue
            break
            ;;
          *)
            _settings_set "${_setting_name}" "${__value}" || continue
            break
            ;;
        esac
      fi
    done
  }

  # Usage: _settings_set (<number> | <name>) <value>
  _settings_set() {
    local _attribution=
    _attribution="# Set by \`${_ME}\` • $(date)"
    local _entry=
    local _setting_name=
    local _stripped=
    local _value=

    _setting_name="$(_settings_identify "${1:-}")"  &&
      if _contains "${2:-}" "unset" "default" "reset"
      then
        _settings_unset "${_setting_name}" && return 0
      fi &&
      _settings_validate_value "${_setting_name}" "${2:-}" &&
      {
        _value="${2:-}"
        if [[ "${_value}" =~ ^~ ]]
        then
          # shellcheck disable=SC2016
          _value="$(printf "%s\\n" "${_value}" | sed "s|~|${HOME}|g")"
        fi
      } &&
      if [[ "${_setting_name}" == "EDITOR" ]]
      then
        _entry="export ${_setting_name}=\"${_value}\" ${_attribution}"
      else
        _entry="export ${_setting_name}=\"\${${_setting_name}:-${_value}}\" ${_attribution}"
      fi &&
      _settings_unset "${_setting_name}" > /dev/null 2>&1     &&
      printf "\\n%s\\n" "${_entry}" >> "${NBRC_PATH}"         &&
      if [[ "${_setting_name}" == "NB_COLOR_PRIMARY"      ]]  ||
         [[ "${_setting_name}" == "NB_COLOR_SECONDARY"    ]]
      then
        _setting_name="$(tput setaf "${_value}")${_setting_name}${_TPUT_SGR0}"
        _value="$(tput setaf "${_value}")${_value}${_TPUT_SGR0}"
      elif [[ "${_setting_name}" == "NB_COLOR_THEME" ]]
      then
        local _new_color=
        _new_color="$(
          NB_COLOR_THEME="${_value}"  \
          NB_COLOR_PRIMARY=''         \
            "${_MY_PATH}" settings get NB_COLOR_PRIMARY
        )"
        _setting_name="$(tput setaf "${_new_color}")${_setting_name}${_TPUT_SGR0}"
        _value="$(tput setaf "${_new_color}")${_value}${_TPUT_SGR0}"
      else
        _setting_name="$(_color_primary "${_setting_name}")"  \
        _value="$(_color_primary "${_value}")"
      fi &&
      printf "%s set to %s\\n"  \
        "${_setting_name}"      \
        "${_value}"
  }

  # Usage: _settings_unset (<number> | <name>)
  _settings_unset() {
    local _setting_name=
    local _default_value=

    _setting_name="$(_settings_identify "${1:-}")"      &&
      _sed_i                                            \
        -e "s/^export ${_setting_name}.*Added by.*$//g" \
        -e "s/^export ${_setting_name}.*Set by.*$//g"   \
        "${NBRC_PATH}" &&
        {
          _stripped=$(<"${NBRC_PATH}")
          printf "%s\\n" "${_stripped}" > "${NBRC_PATH}"
        } && {
          _default_value="$(
            eval "${_setting_name}='' \"${_MY_PATH}\" settings get ${_setting_name}"
          )"

          if [[ "${_setting_name}" == "NB_COLOR_SECONDARY"  ]]
          then
            _setting_name="$(_color_primary "${_setting_name}")"
            _default_value="$(tput setaf "${_default_value}")${_default_value}${_TPUT_SGR0}"
          elif [[ "${_setting_name}" == "NB_COLOR_PRIMARY"  ]]
          then
            _setting_name="$(tput setaf "${_default_value}")${_setting_name}${_TPUT_SGR0}"
            _default_value="$(tput setaf "${_default_value}")${_default_value}${_TPUT_SGR0}"
          elif [[ "${_setting_name}" == "NB_COLOR_THEME"    ]]
          then
            _new_hi_color="$(
              NB_COLOR_THEME="nb" NB_COLOR_PRIMARY='' \
                "${_MY_PATH}" settings get NB_COLOR_PRIMARY
            )"

            _setting_name="$(tput setaf "${_new_hi_color}")${_setting_name}${_TPUT_SGR0}"
            _default_value="$(tput setaf "${_new_hi_color}")${_default_value}${_TPUT_SGR0}"
          else
            _setting_name="$(_color_primary "${_setting_name}")"
            _default_value="$(_color_primary "${_default_value}")"
          fi
        } &&
        printf "%s restored to the default: %s\\n"  \
          "${_setting_name}"                        \
          "${_default_value}"
  }

  # Usage: _settings_validate_value (<number> | <name>) <value>
  _settings_validate_value() {
    if [[ "${1:-}" == 'EDITOR'                ]] ||
       [[ "${1:-}" == 'NB_DEFAULT_EXTENSION'  ]]
    then
      if [[ -n "${2:-}" ]]
      then
        return 0
      else
        _warn printf "%s requires a value.\\n" "${1:-}" &&
          return 1
      fi
    elif [[ "${1:-}" == 'NB_AUTO_SYNC'  ]] ||
         [[ "${1:-}" == 'NB_FOOTER'     ]]
    then
      if [[ "${2:-}" =~ ^0$|^1$         ]]
      then
        return 0
      else
        _warn printf "%s must be either \"0\" or \"1\".\\n" "${1}" &&
          return 1
      fi
    elif [[ "${1:-}" == 'NB_HEADER'     ]]
    then
      if [[ "${2:-}" =~ ^0$|^1$|^2$|^3$ ]]
      then
        return 0
      else
        _warn printf "%s must be \"0\", \"1\", \"2\", or \"3\".\\n" "${1}" &&
          return 1
      fi
    elif [[ "${1:-}" == 'NB_LIMIT'      ]]
    then
      if [[ "${2:-}" =~ ^[0-9]+$        ]]
      then
        return 0
      else
        _warn printf "%s must be a number.\\n" "${1}" &&
          return 1
      fi
    elif [[ "${1:-}" == 'NB_DIR'        ]]
    then
      if [[ -z "${2:-}"   ]] ||
         [[ "${2}" == "/" ]] ||
         {
          [[ -e "${2}"   ]] &&
          [[ ! -w "${2}" ]]
         }
      then
        _warn printf "\"%s\" is not a valid location for NB_DIR.\\n" "${2:-}" &&
          return 1
      fi
    elif [[ "${1:-}" == 'NB_ENCRYPTION_TOOL'  ]]
    then
      if [[ "${2:-}" =~ ^openssl$|^gpg$       ]]
      then
        return 0
      else
        _warn \
          printf "NB_ENCRYPTION_TOOL must be either \"openssl\" or \"gpg\".\\n" &&
            return 1
      fi
    elif [[ "${1:-}" == 'NB_COLOR_PRIMARY'    ]] ||
         [[ "${1:-}" == 'NB_COLOR_SECONDARY'  ]]
    then
      if [[ "${2:-}" =~ ^[0-9] ]]
      then
        return 0
      else
        _warn printf "%s must be a number.\\n" "${1}" && return 1
      fi
    elif [[ "${1:-}" == 'NB_COLOR_THEME'      ]]
    then
      if _contains "${2:-}" "${_NB_COLOR_THEMES[@]}"
      then
        return 0
      else
        _warn printf "%s must be one of the available themes.\\n" "${1}" &&
          return 1
      fi
    elif [[ "${1:-}" == 'NB_SYNTAX_THEME' ]]
    then
      if ! hash bat 2>/dev/null
      then
        printf "\`bat\` required: https://github.com/sharkdp/bat\\n"
      else
        local _theme_list
        _theme_list=($(bat --list-themes --color never))

        if _contains "${2:-}" "${_theme_list[@]}"
        then
          return 0
        else
          _warn printf "%s must be one of the available themes.\\n" "${1}" &&
          return 1
        fi
      fi
    else
      return 0
    fi
  }

  local _settings_list_items=()
  local _settings_help_items=()

  _settings_list_items+=("$(_color_brackets '1')  auto_sync")
  _settings_help_items+=("\
    $(_color_secondary " ---------")
     By default, operations that trigger a git commit like \`add\`, \`edit\`,
     and \`delete\` will sync notebook changes to the remote repository, if
     one is set. To disable this behavior, set this to \"0\".

     • Default Value: $(_color_primary "1")")

  _settings_list_items+=("$(_color_brackets '2')  color_primary")
  _settings_help_items+=("\
    $(_color_secondary " -------------")
     The primary color used to highlight identifiers and messages. Often this
     can be set to an xterm color number between 0 and 255. Some terminals
     support many more colors.

     • Default Value: $(_color_primary "68") (blue) for 256 color terminals,
                      $(_color_primary "4")  (blue) for  8  color terminals.")

  _settings_list_items+=("$(_color_brackets '3')  color_secondary")
  _settings_help_items+=("\
    $(_color_secondary " ---------------")
     The color used for lines and footer elements. Often this can be set to an
     xterm color number between 0 and 255. Some terminals support many more
     colors.

     • Default Value: $(_color_primary "8")")

  _settings_list_items+=("$(_color_brackets '4')  color_theme")
  _settings_help_items+=("\
    $(_color_secondary " -----------")
     The color theme.

     To view screenshots of the built-in themes, visit:

         $(_color_primary "https://git.io/nb-docs-color-themes")

     \`${_ME}\` supports custom, user-defined themes. To learn more, run:

         $(_color_primary "${_ME} help --colors")

     To change the syntax highlighting theme, use:

         $(_color_primary "${_ME} set syntax_theme")

     • Available themes:

$(for __theme in "${_NB_COLOR_THEMES[@]}"
  do
    printf "         %s\\n" "$(_color_primary "${__theme}")"
  done)

     • Default Value: $(_color_primary "nb")")

  _settings_list_items+=("$(_color_brackets '5')  default_extension")
  _settings_help_items+=("\
    $(_color_secondary " -----------------")
     The default extension to use for note files. Change to \"org\" for Emacs
     Org mode files, \"rst\" for reStructuredText, \"txt\" for plain text, or
     whatever you prefer.

     • Default Value: $(_color_primary "md")")

  _settings_list_items+=("$(_color_brackets '6')  editor")
  _settings_help_items+=("\
    $(_color_secondary " ------")
     The command line text editor to use with \`${_ME}\`.

     • Example Values:

$(for __editor in atom code emacs macdown mate micro nano pico subl vi vim
  do
    if _command_exists "${__editor}"
    then
      printf "         %s\\n" "$(_color_primary "${__editor}")"
    fi
  done)")

  _settings_list_items+=("$(_color_brackets '7')  encryption_tool")
  _settings_help_items+=("\
    $(_color_secondary " ---------------")
     The tool used for encrypting notes.

     • Supported Values: $(_color_primary "openssl"), $(_color_primary "gpg")
     • Default Value:    $(_color_primary "openssl")")

  _settings_list_items+=("$(_color_brackets '8')  footer")
  _settings_help_items+=("\
    $(_color_secondary " ------")
     By default, \`nb\` and \`${_ME} ls\` include a footer with example commands.
     To hide this footer, set this to \"0\".

     • Default Value: $(_color_primary "1")")

  _settings_list_items+=("$(_color_brackets '9')  header")
  _settings_help_items+=("\
    $(_color_secondary " ------")
     By default, \`nb\` and \`${_ME} ls\` include a header listing available notebooks.
     Set the alignment, or hide the header with \"0\".

     • Supported Values:

       $(_color_primary "0")  Hide Header
       $(_color_primary "1")  Dynamic Alignment
            - Left justified when list is shorter than terminal width.
            - Center aligned when list is longer than terminal width.
       $(_color_primary "2")  Center Aligned (default)
       $(_color_primary "3")  Left Justified

     • Default Value: $(_color_primary "2")")

  _settings_list_items+=("$(_color_brackets '10') limit")
  _settings_help_items+=("\
    $(_color_secondary " -----")
     The maximum number of notes included in the \`${_ME}\` and \`${_ME} ls\` lists.

     • Default Value: $(_color_primary "20")")

  _settings_list_items+=("$(_color_brackets '11') nb_dir")
  _settings_help_items+=("\
    $(_color_secondary " ------")
     The location of the directory that contains the notebooks.

     For example, to sync all notebooks with Dropbox, create a folder at
     \`~/Dropbox/Notes\` and run: \`${_ME} settings set nb_dir ~/Dropbox/Notes\`

     • Default Value: $(_color_primary '~/.nb')")

  _settings_list_items+=("$(_color_brackets '12') syntax_theme")
  _settings_help_items+=("\
    $(_color_secondary " ------------")
     The syntax highlighting theme. View examples with:

         $(_color_primary 'bat --list-themes')

     $(if ! hash "bat" 2>/dev/null
       then
         printf "Install \`bat\` to enable themes: https://github.com/sharkdp/bat\\n"
       else
         printf "• Available themes:\\n"
         printf "\\n"

         for __theme in $(bat --list-themes --color never)
        do
          printf "         %s\\n" "$(_color_primary "${__theme}")"
        done
      fi)

     • Default Value: $(_color_primary 'base16')")

  while ((${#}))
  do
    local __arg="${1:-}"

    case "${__arg}" in
      color|colors)
        _settings_colors "${2:-}"
        return 0
        ;;
      edit)
        _edit_file "${NBRC_PATH}" --no-wait
        return 0
        ;;
      get)
        if [[ -z "${2:-}" ]]
        then
          _exit_1 _help settings
        fi

        _settings_get_value "${2:-}"
        return 0
        ;;
      list)
        if [[ "${2:-}" == "--long" ]]
        then
          local _counter=0

          for __setting in "${_settings_list_items[@]}"
          do
            ((_counter)) && printf "\\n"
            _counter="$((_counter+1))"

            _settings show "${_counter}"
          done
        else
          printf "%s\\n" "${_settings_list_items[@]}"
        fi

        return 0
        ;;
      set)
        if [[ -z "${2:-}" ]] || [[ -z "${3:-}" ]]
        then
          _exit_1 _help "settings"
        fi

        _settings_set "${2:-}" "${3:-}"
        return 0
        ;;
      show|help|about|info|more)
        if [[ -z "${2:-}" ]]
        then
          _exit_1 _help "settings"
        fi

        local _setting_id=
        _setting_id="$(_settings_identify --id "${2:-}")"

        local _setting_name=
        _setting_name="$(_settings_identify "${2:-}")"

        local _index=
        _index="$((_setting_id-1))"

        printf "%s\\n%s\\n"                       \
          "${_settings_list_items[${_index}]}"    \
          "${_settings_help_items[${_index}]}"

        printf "\\n%s is currently set to %s\\n"  \
          "$(_color_primary "${_setting_name}")"  \
          "$(_color_primary "$(_settings_get_value "${_setting_name}")")"
        return 0
        ;;
      reset|unset)
        if [[ -z "${2:-}" ]]
        then
          _exit_1 _help "settings"
        fi

        _settings_unset "${2:-}"
        return 0
        ;;
      *)
        local _setting_name=
        if [[ -n "${1:-}" ]]
        then
          _setting_name="$(_settings_identify "${1}")"
        fi

        if [[ -n "${_setting_name:-}" ]]
        then
          if [[ -n "${2:-}" ]]
          then
            _settings_set "${_setting_name}" "${2}"
          else
            _settings_prompt "${_setting_name}"
          fi
        else
          _settings_prompt
        fi

        return 0
        ;;
    esac

    shift
  done
}
_alias_subcommand "settings" "config"
_alias_subcommand "settings" "set"

# shell  ################################################################ shell

describe "shell" <<HEREDOC
Usage:
  ${_ME} shell [<subcommand> [<options>...] | --clear-history]

Optons:
  --clear-history  Clear the \`${_ME}\` shell history.

Description:
  Start the \`${_ME}\` interactive shell. Type "exit" to exit.

  \`${_ME} shell\` recognizes all \`${_ME}\` subcommands and options, providing
  a streamlined, distraction-free approach for working with \`${_ME}\`.

  When <subcommand> is present, the command will run as the shell is opened.

Example:
  $ ${_ME} shell
  ${_ME}> ls 3
  [3] Example

  ${_ME}> edit 3 --content "New content."
  Updated [3] Example

  ${_ME}> notebook
  home

  ${_ME}> exit
  $
HEREDOC
_shell() {
  HISTFILE="${HOME}/.${_ME}_history"
  set -o history

  local _initial_command=
  local _prompt=
  _prompt="$(_color_primary "${_ME}")$(_color_secondary ">") "

  for __arg in "${@:-}"
  do
    case "${__arg}" in
      --clear-history)
        if [[ -e "${HISTFILE}" ]]
        then
          rm "${HISTFILE:?}"
          printf "History cleared.\\n"
          return 0
        fi
        ;;
      *)
        if [[ -n "${__arg}" ]]
        then
          _initial_command+=" ${__arg}"
        fi
        ;;
    esac
  done

  printf "%s" "${_TPUT_COLOR_PRIMARY}"
  cat <<HEREDOC
__          _
\ \   _ __ | |__
 \ \ | '_ \| '_ \\
 / / | | | | |_) |
/_/  |_| |_|_.__/
$(_color_secondary "------------------")
$(_color_primary "${_ME} shell") started. Enter $(_color_primary "ls") to list notes and notebooks.
Enter $(_color_primary "help") for usage information. Enter $(_color_primary "exit") to exit.
HEREDOC

  if [[ -n "${_initial_command:-}" ]]
  then
    eval "\"${_MY_PATH}\" ${_initial_command}"
  fi

  while IFS='' read -r -e -d $'\n' -p "${_prompt}" __input
  do
    history -s "${__input}"

    if [[ "${__input}" =~ ^exit$|^quit$|^q$ ]]
    then
      return 0
    else
      # Strip leading 'n', 'nb', or 'notes'.
      local _normalized_input=
      _normalized_input="$(
        printf "%s\\n" "${__input}" \
          | sed -e 's/^n$//'        \
          | sed -e 's/^n //'        \
          | sed -e 's/^nb$//'       \
          | sed -e 's/^nb //'       \
          | sed -e 's/^notes$//'    \
          | sed -e 's/^notes //'
      )"

      if [[ "${_normalized_input}" =~ ^help   ]] ||
         [[ "${_normalized_input}" =~ ^help\  ]] ||
         [[ "${_normalized_input}" =~ ^h$     ]] ||
         [[ "${_normalized_input}" =~ ^h\     ]]
      then
        eval "\"${_MY_PATH}\" ${_normalized_input}" --shell || :
      elif [[ "${_normalized_input}" =~ ^echo\ .*   ]] ||
           [[ "${_normalized_input}" =~ ^printf\ .* ]]
      then
        eval "${_normalized_input}"
      elif _command_name="$(
        printf "%s\\n" "${_normalized_input}" \
          | grep --only-matching --color=never \
              -e '^code'    \
              -e '^emacs'   \
              -e '^gvim'    \
              -e '^macdown' \
              -e '^mate'    \
              -e '^mvim'    \
              -e '^nano'    \
              -e '^nvim'    \
              -e '^subl'    \
              -e '^vim'
      )"
      then
        _normalized_input="$(
          printf "%s\\n" "${_normalized_input}" \
            | sed -e "s/^${_command_name} //"
        )"

        eval "\"${_MY_PATH}\" edit --editor \"${_command_name}\" ${_normalized_input}"
      else
        eval "\"${_MY_PATH}\" ${_normalized_input}" || :
      fi
    fi
  done
}

# show ################################################################### show

# TODO: Vim highlighting bug. "\`

describe "show" <<HEREDOC
Usage:
  ${_ME} show (<id> | <filename> | <path> | <title>) [[-a | --added] |
          --filename | --id | --info-line | --path | [-p | --print]
          [-r | --render] | --selector-id | --title | --type [<type>] |
          [-u | --updated]]
  ${_ME} show <notebook>

Options:
  -a, --added      Print the date and time when the item was added.
  --filename       Print the filename of the item.
  --id             Print the id number of the item.
  --info-line      Print the id, filename, and title of the item.
  --path           Print the full path of the item.
  -p, --print      Print to standard output / terminal.
  -r, --render     Use \`pandoc\` [1] to render the file to HTML and display
                   in the terminal web browser. If either \`pandoc\` or a
                   browser are unavailable, \`-r\` / \`--render\` is ignored.
  --selector-id    Given a selector (e.g., notebook:example.md), print the
                   identifier portion (example.md).
  --title          Print the title of the note.
  --type [<type>]  Print the file extension or, when <type> is specified,
                   return true if the item matches <type>. <type> can be a
                   file extension or one of the following types:
                   archive, audio, bookmark, document, folder, image,
                   text, video
  -u, --updated    Print the date and time of the last recorded change.

Description:
  Show an item or notebook. Notes in text file formats can be rendered or
  printed to standard output. Non-text files will be opened in your system's
  preferred app or program for that file type.

  By default, the item will be opened using \`less\` or the program configured
  in the \`\$PAGER\` environment variable. Use the following keys to navigate
  in \`less\` (see \`man less\` for more information):

    Key               Function
    ---               --------
    mouse scroll      Scroll up or down
    arrow up or down  Scroll one line up or down
    f                 Jump forward one window
    b                 Jump back one window
    d                 Jump down one half window
    u                 Jump up one half window
    /<query>          Search for <query>
    n                 Jump to next <query> match
    q                 Quit

  To skip the pager and print to standard output, use the \`-p\` / \`--print\`
  option.

  \`-r\` / \`--render\` automatically uses either \`w3m\` [2] or \`lynx\` [3].
  To specify a preferred browser, set the \`\$BROWSER\` environment variable
  in your .bashrc, .zshrc, or equivalent, e.g., \`export BROWSER="lynx"\`.

  If \`bat\` [4], \`highlight\` [5], or Pygments [6] is installed, notes are
  printed with syntax highlighting.

    1. https://pandoc.org/
    2. https://en.wikipedia.org/wiki/W3m
    3. https://en.wikipedia.org/wiki/Lynx_(web_browser)
    4. https://github.com/sharkdp/bat
    5. http://www.andre-simon.de/doku/highlight/en/highlight.php
    6. https://pygments.org/

Examples:
  ${_ME} show 1
  ${_ME} show example.md --render
  ${_ME} show "A Document Title" --print --no-color
  ${_ME} 1 show
  ${_ME} example:show 12
  ${_ME} show example:12
  ${_ME} example:12 show
  ${_ME} s 1
  ${_ME} 1 s
  ${_ME} s example:12
  ${_ME} example:12 s

Alias: \`view\`
Shortcut Alias: \`s\`
HEREDOC
_show() {
  # _show_indicators()
  #
  # Usage:
  #   _show_indicators <path>
  #
  # Description:
  #   Print a list of indicators based on the file type.
  #
  # NOTE: Indicator logic is also specified in `list` for performance.
  _show_indicators() {
    local _indicators=
    local _path="${1:-}"

    if _file_is_bookmark "${_path}"
    then
      _indicators+="🔖 "
    elif _file_is_image "${_path}"
    then
      _indicators+="🌄 "
    elif _file_is_document "${_path}"
    then
      _indicators+="📄 "
    elif [[ -d "${_path}" ]]
    then
      _indicators+="📂 "
    elif _file_is_video "${_path}"
    then
      _indicators+="📹 "
    elif _file_is_audio "${_path}"
    then
      _indicators+="🔉 "
    elif [[ "${_path}" =~ \.epub$ ]]
    then
      _indicators+="📖 "
    fi

    if _file_is_encrypted "${_path}"
    then
      _indicators+="🔒 "
    fi

    printf "%s\\n" "${_indicators:-}"
  }

  # _show_selector_basename()
  #
  # Usage:
  #   _show_selector_basename <selector> [<notebook-path>]
  #
  # Description:
  #   Determine the basename of the file that is identified by the given <id>,
  #   <filename>, <path>, or <title> and is in the current notebook or a
  #   notebook specified with a colon prefix.
  #
  #   Examples:
  #     1
  #     example.md
  #     title
  #     /path/to/example.md
  #     notebook:1
  #     notebook:example.md
  #     notebook:title
  #     notebook:/path/to/example.md
  _show_selector_basename() {
    local _basename=
    local _identifier=
    local _selector="${1:-}"
    local _notebook_path="${2:-}"

    if [[ -z "${_selector}" ]]
    then
      _basename=
    else
      if [[ -z "${_notebook_path:-}" ]]
      then
        _notebook_path="$(
          _notebooks show "${_selector}" --path
        )"
      fi

      _identifier="$(_show "${_selector}" --selector-id)"

      if [[ -e "${_identifier}"                   ]] &&
         [[ "${_identifier}" =~ ${_notebook_path} ]]
      then # <selector> is a full path.
        _basename="$(basename "${_identifier}")"
      elif [[ -e "${_notebook_path}/${_identifier}" ]]
      then # <selector> is a filename.
        _basename="${_identifier}"
      elif [[ "${_identifier}" =~ ^[0-9]+$ ]]
      then # <selector> is an id.
        _basename="$(_index get_basename "${_identifier}")"
      else # <selector> might be a title.
        local _title=
        local _filenames
        _filenames=($(_list_files "${_notebook_path}"))

        for __file in "${_filenames[@]:-}"
        do
          _title="$(_get_title "${_notebook_path}/${__file}")"

          if [[ -n "${_title}"                  ]] &&
             [[ "${_title}" == "${_identifier}" ]]
          then
            _basename="${__file}"
            break
          fi
        done
      fi
    fi

    printf "%s\\n" "${_basename}"
  }

  local _ls_arguments=()
  local _print_output=0
  local _password=
  local _print_added=0
  local _print_filename=0
  local _print_id=0
  local _print_info=0
  local _print_path=0
  local _print_selector_id=0
  local _print_updated=0
  local _render=0
  local _selector=
  local _show_color_enabled="${_COLOR_ENABLED}"
  local _skip_notebook_fallback=0
  local _print_indicators=0
  local _print_title=0
  local _tool=
  local _type=
  local _type_check=0

  while ((${#}))
  do
    local __arg="${1:-}"
    local __val="${2:-}"

    case "${__arg}" in
      --path)
        _print_path=1
        _skip_notebook_fallback=1
        ;;
      -a|--added*)
        _print_added=1
        _skip_notebook_fallback=1
        ;;
      --filename|--basename)
        _print_filename=1
        _skip_notebook_fallback=1
        ;;
      --id|--index)
        _print_id=1
        _skip_notebook_fallback=1
        ;;
      --indicators)
        _print_indicators=1
        ;;
      --info*line)
        _print_info=1
        _skip_notebook_fallback=1
        ;;
      --no*color|--raw)
        _show_color_enabled=0
        ;;
      --password)
        _password="$(_option_get_value "${__arg}" "${__val:-}")"
        shift
        ;;
      -p|--print|--dump|--skip*pager|--no*pager)
        _print_output=1
        ;;
      -r|--render)
        _render=1
        _ls_arguments+=("${__arg}")
        ;;
      --selector*id)
        _print_selector_id=1
        _skip_notebook_fallback=1
        ;;
      --title)
        _print_title=1
        _skip_notebook_fallback=1
        ;;
      --tool)
        _tool="$(_option_get_value "${__arg}" "${__val:-}")"
        shift
        ;;
      --type)
        _type_check=1
        _skip_notebook_fallback=1

        if _option_value_is_present "${__val:-}"
        then
          _type="${__val}"
          shift
        fi
        ;;
      -u|--updated*)
        _print_updated=1
        _skip_notebook_fallback=1
        ;;
      *)
        if [[ -z "${_selector:-}" ]]
        then
          _selector="${__arg}"
        fi

        _ls_arguments+=("${__arg}")
        ;;
    esac

    shift
  done

  if [[ -z "${_selector:-}" ]]
  then
    _exit_1 _help "show"
  fi

  if ((_print_selector_id))
  then
    printf "%s\\n" "${_selector#*:}"
    return 0
  fi

  local _notebook_path
  _notebook_path="$(_notebooks current --path)"

  local _basename
  _basename="$(_show_selector_basename "${_selector}" "${_notebook_path}")"

  local _target_path="${_notebook_path}/${_basename}"

  if [[ -z "${_basename:-}" ]] && ! ((_skip_notebook_fallback))
  then
    local _maybe_notebook="${_selector:-}"
    _maybe_notebook="$(printf "%s\\n" "${_maybe_notebook}" | sed 's/\:$//')"

    if [[ -d "${NB_DIR}/${_maybe_notebook}/.git" ]]
    then
      NB_NOTEBOOK_PATH="${NB_DIR}/${_maybe_notebook}" \
        _ls "${_ls_arguments[@]:1}"
      return 0
    else
      _warn printf "Not found: %s\\n" "$(_color_primary "${_selector}")"
      return 1
    fi
  fi

  if [[ -z "${_basename:-}" ]] || [[ ! -e "${_target_path}" ]]
  then
    _warn printf "Not found: %s\\n" "$(_color_primary "${_selector:-}")"
    return 1
  fi

  if ((_print_path))
  then # `show --path`
    printf "%s/%s\\n" "${_notebook_path}" "${_basename}"
    return 0
  fi

  if ((_print_filename))
  then
    printf "%s\\n" "${_basename}"
    return 0
  fi

  if ((_print_id))
  then
    printf "%s\\n" "$(_index get_id "${_basename}")"
    return 0
  fi

  if ((_print_title))
  then
    _get_title "${_notebook_path}/${_basename}"
    return 0
  fi

  if ((_print_indicators))
  then
    _show_indicators "${_notebook_path}/${_basename}"
    return 0
  fi

  if ((_type_check))
  then
    if [[ -z "${_type}" ]]
    then
      printf "%s\\n" "${_basename#*.}"
      return 0
    else
      if _contains "${_type}" archive audio bookmark document folder image text video
      then
        "_file_is_${_type}" "${_notebook_path}/${_basename}"
        return $?
      else
        [[ "${_type}" == "${_basename#*.}" ]]
        return $?
      fi
    fi
  fi

  if ((_print_info))
  then
    local _id
    _id="$(_index get_id "${_basename}")"

    local _maybe_scoped_id
    _maybe_scoped_id="${_id}"

    local _maybe_scoped_basename="${_basename}"

    if _notebooks current --selected
    then
      local _notebook_escaped_name
      _notebook_escaped_name="$(
        _notebooks show "${_notebook_path}" --escaped --no-color
      )"

      _maybe_scoped_id="${_notebook_escaped_name}:${_maybe_scoped_id}"
      _maybe_scoped_basename="${_notebook_escaped_name}:${_maybe_scoped_basename}"
    fi

    local _info_line=
    _info_line+="$(_color_brackets "${_maybe_scoped_id}") "

    local _indicators
    _indicators="$(_show "${_notebook_path}/${_basename}" --indicators)"

    if [[ -n "${_indicators:-}" ]]
    then
      _info_line+="${_indicators}"
    fi

    _info_line+="$(_color_primary "${_maybe_scoped_basename}") "

    local _title
    _title="$(_get_title "${_notebook_path}/${_basename}")"


    if [[ -n "${_title:-}" ]]
    then
      _info_line+="\"${_title}\""
    fi

    # trim leading and trailing whitepace
    IFS=' ' read -r _info_line <<<"${_info_line:-}"

    printf "%s\\n" "${_info_line:-}"
    return 0
  fi

  if ((_print_added))
  then
    git -C "${_notebook_path}" --no-pager \
      log                                 \
      --follow                            \
      --diff-filter=A                     \
      --date=iso-local                    \
      --pretty='%ad'                      \
      "${_basename}"

    return 0
  fi

  if ((_print_updated))
  then
    git -C "${_notebook_path}" --no-pager \
      log                                 \
      -1                                  \
      --follow                            \
      --date=iso-local                    \
      --pretty='%ad'                      \
      "${_basename}"

    return 0
  fi

  if _file_is_encrypted "${_target_path}"
  then
    local _encrypted_path
    _encrypted_path="${_target_path}"

    if [[ -z "${_password}" ]]
    then
      # request password without displaying it
      printf "%s: " "$(_color_primary "Password")"
      read -r -s _password </dev/tty
      printf "\\n" # print newline to stop `read`
    fi

    if [[ -z "${_password}" ]]
    then
      _exit_1 printf "Password required.\\n"
    fi

    local _decrypted_path
    _decrypted_path="$(_decrypt_file "${_target_path}" "${_password}")"

    _target_path="${_decrypted_path}"
  fi

  if _file_is_archive "${_target_path}" &&
     [[ ! "${_target_path}" =~ docx$ ]]
  then
    _exit_1 printf "\
Can't show archives. Export archive and expand to edit.\\n"
  fi

  if ! _file_is_text "${_target_path}"
  then
    if [[ -d "${_target_path}"      ]] &&
       _command_exists "ranger"        &&
       {
         [[ -z "${_tool}"           ]] ||
         [[ "${_tool}" == "ranger"  ]]
       }
    then
      ranger "${_target_path}"
    elif [[ -d "${_target_path}" ]] &&
         _command_exists "mc"       &&
         {
           [[ -z "${_tool}"      ]] ||
           [[ "${_tool}" == "mc" ]]
         }
    then
      mc "${_target_path}"
    elif [[ -d "${_target_path}"  ]] &&
         _command_exists "exa"       &&
         {
           [[ -z "${_tool}"       ]] ||
           [[ "${_tool}" == "exa" ]]
         }
    then
      exa -lah --git "${_target_path}"
    elif [[ -d "${_target_path}" ]]
    then
      # gnu || bsd
      ls -lah --color=always "${_target_path}" 2>/dev/null ||
        ls -lah -G "${_target_path}"
    elif _command_exists "imgcat"                 &&
         _file_is_image "${_target_path}"         &&
         [[ ! "${_target_path}" =~ afphoto$   ]]  &&
         [[ ! "${_target_path}" =~ svg$       ]]  &&
         [[ -n "${TERM_PROGRAM:-}"            ]]  &&
         [[ "${TERM_PROGRAM}" == "iTerm.app"  ]]  &&
         {
           [[ -z "${_tool}"          ]] ||
           [[ "${_tool}" == "imgcat" ]]
         }
    then
      imgcat "${_target_path}" \
        | if _command_exists "less" && ! ((_print_output))
          then
            less -r --prompt="$(_less_prompt)"
          else
            cat
          fi
    elif _command_exists "magick"               &&
         _file_is_image "${_target_path}"       &&
         [[ ! "${_target_path}" =~ afphoto$  ]] &&
         {
           # Detect sixel support. Via: https://git.io/JfCel
           IFS=";" read -ra __reply -s -t 1 -d "c" -p $'\e[c' >&2
           [[ "${__reply[*]}" =~ 4 ]] || [[ "$TERM" == yaft* ]]
         } && {
           [[ -z "${_tool}"          ]] ||
           [[ "${_tool}" == "magick" ]]
         }
    then
      magick "${_target_path}" sixel:- \
        | if _command_exists "less" && ! ((_print_output))
          then
            less -r --prompt="$(_less_prompt)"
          else
            cat
          fi
    elif [[ -n "${KITTY_WINDOW_ID:-}" ]]  &&
         _file_is_image "${_target_path}" &&
         {
           [[ -z "${_tool}"           ]]  ||
           [[ "${_tool}" == "icat"    ]]
         }
    then
      kitty +kitten icat "${_target_path}" \
        | if _command_exists "less" && ! ((_print_output))
          then
            less -r --prompt="$(_less_prompt)"
          else
            cat
          fi
    elif _command_exists "mplayer"        &&
         _file_is_audio "${_target_path}" &&
         {
           [[ -z "${_tool}"           ]] ||
           [[ "${_tool}" == "mplayer" ]]
         }
    then
      printf "Playing audio. Press %s to quit.\\n" "$(_color_primary "Ctrl-C")"
      mplayer "${_target_path}"
    elif _command_exists "afplay"         &&
         _file_is_audio "${_target_path}" &&
         {
           [[ -z "${_tool}"          ]] ||
           [[ "${_tool}" == "afplay" ]]
         }
    then
      printf "Playing audio. Press %s to quit.\\n" "$(_color_primary "Ctrl-C")"
      afplay "${_target_path}"
    elif _command_exists "mpg123"                   &&
         _file_is_audio "${_target_path}"           &&
         [[ "${_target_path}" =~ mp1$|mp2$|mp3$ ]]  &&
         {
           [[ -z "${_tool}"          ]] ||
           [[ "${_tool}" == "mpg123" ]]
         }
    then
      printf "Playing audio. Press %s to quit.\\n" "$(_color_primary "Ctrl-C")"
      mpg123 "${_target_path}"
    elif _command_exists "ffplay"         &&
         _file_is_audio "${_target_path}" &&
         {
           [[ -z "${_tool}"          ]] ||
           [[ "${_tool}" == "ffplay" ]]
         }
    then
      printf "Playing audio. Press %s to quit.\\n" "$(_color_primary "Ctrl-C")"
      ffplay -loglevel quiet "${_target_path}"
    elif _command_exists "termpdf.py"     &&
         [[ "${_target_path}" =~ pdf$ ]]  &&
         [[ -n "${KITTY_WINDOW_ID:-}" ]]
    then
      termpdf.py "${_target_path}"
    elif _command_exists "pdftotext" && [[ "${_target_path}" =~ pdf$ ]]
    then
      pdftotext "${_target_path}" - \
        | tr -d '\014'              \
        | if ! ((_print_output))
          then
            _pager
          else
            cat
          fi
    elif [[ "${_target_path}" =~ docx$ ]] && _command_exists "pandoc"
    then
      pandoc --from docx --to markdown "${_target_path}"  \
        | _highlight_syntax_if_available                  \
        | if ! ((_print_output))
          then
            _pager
          else
            cat
          fi
    elif [[ "${_target_path}" =~ epub$ ]] &&
         _command_exists "pandoc"         &&
         _browser
    then
      pandoc -f epub -t html "${_target_path}" | _browser
    elif _command_exists "xdg-open"
    then
      xdg-open "${_target_path}"
    elif [[ "${OSTYPE}" =~ ^darwin ]]
    then
      open "${_target_path}"
    fi
  elif ((_render))              &&
       ((_print_output))        &&
       _command_exists "pandoc" &&
       _browser
  then # `show --render --print` with `pandoc` and browser available
    pandoc "${_target_path}" | _browser --dump
  elif ((_render))              &&
       _command_exists "pandoc" &&
       _browser
  then # `show --render` with `pandoc` and browser available
    pandoc "${_target_path}" | _browser
  else # default
    if ((_print_output))
    then # `show --print [--no-color]`
      if ((_show_color_enabled))
      then # `show --print`
        _highlight_syntax_if_available "${_target_path}"
      else # `show --print --no-color`
        cat "${_target_path}"
      fi
    else # `show`
      # NOTE: Temp character is U+241F (unit separator)
      # https://graphemica.com/%E2%90%9F
      < "${_target_path}" sed 's/^- /-␟/g'                        \
        | sed 's/^\* /\*␟/g'                                      \
        | fold -s -w "$(tput cols)"                               \
        | sed 's/^-␟/- /g'                                        \
        | sed 's/^\*␟/\* /g'                                      \
        | _highlight_syntax_if_available "${_target_path##*.}"    \
        | _pager
    fi
  fi

  if [[ -n "${_decrypted_path:-}" ]] && [[ -e "${_decrypted_path}" ]]
  then
    rm "${_decrypted_path:?}"
  fi
}
_alias_subcommand "show" "s"
_alias_subcommand "show" "view"

# status ############################################################### status

describe "status" <<HEREDOC
Usage:
  ${_ME} status

Description:
  Run \`git status\` in the current notebook.
HEREDOC
_status() {
  local _notebook_path
  _notebook_path="$(_notebooks current --path)"

  git -C "${_notebook_path}" status
}

# subcommands ##################################################### subcommands

describe "subcommands" <<HEREDOC
Usage:
  ${_ME} subcommands [add <name>...] [alias <name> <alias>]
                 [describe <name> <usage>]

Subcommands:
  add       Add a new subcommand.
  alias     Create an <alias> of a given subcommand <name>, with linked help.
            Note that aliases must also be added with \`subcommands add\`.
  describe  Set the usage text displayed with \`${_ME} help <subcommand>\`.
            This can be assigned as a heredoc, which is recommended, or
            as a string argument.

Description:
  List, add, alias, and describe subcommands. New subcommands, aliases, and
  descriptions are not persisted, so \`add\`, \`alias\`, \`describe\` are
  primarily for plugins.
HEREDOC
_subcommands() {
  case "${1:-}" in
    add)
      if [[ -n "${2:-}" ]]
      then
        shift

        NB_PLUGIN_SUBCOMMANDS+=("${@:-}")
      fi
      ;;
    alias)
      if [[ -n "${2:-}" ]] &&
         [[ -n "${3:-}" ]]
      then
        _alias_subcommand "${2:-}" "${3:-}"
      fi
      ;;
    describe)
      shift

      describe "${@:-}"
      ;;
    *)
      printf "%s\\n" "${_DOCUMENTED_SUBCOMMANDS[*]}"
      ;;
  esac
}
_alias_subcommand "subcommands" "commands"

# sync ################################################################### sync

describe "sync" <<HEREDOC
Usage:
  ${_ME} sync [-a | --all]

Options:
  -a, --all   Sync all unarchived notebooks.

Description:
  Sync the current local notebook with the remote repository.

Private Repositories and Git Credentials:
  Syncing with private repositories requires configuring git to not prompt
  for credentials.

  For repositories cloned over HTTPS, credentials can be cached with git.
  For repositories cloned over SSH, keys can be added to the ssh-agent.

  More Information:
    https://github.com/xwmx/nb#private-repositories-and-git-credentials

Sync Conflict Resolution:
  When \`${_ME} sync\` encounters a conflict in a text file and can't merge
  overlapping local and remote changes, both versions are saved in the
  file, separated by git conflict markers. Use \`${_ME} edit\` to remove the
  conflict markers and delete any unwanted text.

  When \`${_ME} sync\` encounters a conflict in a binary file, such as an
  encrypted note or bookmark, both versions of the file are saved in the
  notebook as individual files, one with \`--conflicted-copy\` appended to
  the filename.

  More Information:
    https://github.com/xwmx/nb#sync-conflict-resolution
HEREDOC
_sync() {
  local _notebooks=()
  local _sync_all=0

  if [[ "${1:-}" =~ ^-a$|^--all$ ]]
  then
    _sync_all=1

    local _notebook_names
    _notebooks=($(_notebooks --names --no-color --unarchived))
  else
    _notebooks=($(_notebooks current))
  fi

  local _sync_successful=0
  local _sync_started=0
  local _syncable_count=0

  for __notebook in "${_notebooks[@]:-}"
  do
    if [[ "${__notebook}" == "local"      ]] &&
       [[ -n "${_LOCAL_NOTEBOOK_PATH:-}"  ]]
    then
      NB_NOTEBOOK_PATH="${_LOCAL_NOTEBOOK_PATH}"
    else
      NB_NOTEBOOK_PATH="${NB_DIR}/${__notebook}"
    fi

    if ((_sync_all))
    then
      _remote &>/dev/null || continue
    else
      _remote 1>/dev/null || {
        cat <<HEREDOC

Set the remote for the current notebook:

  $(_color_primary "${_ME} remote set <url>")

Set the remote for a notebook named "example":

  $(_color_primary "${_ME} example:remote set <url>")

HEREDOC
        return 1
      }
    fi

    if ! ((_sync_started))
    then
      printf "Syncing: "
      _sync_started=1
    fi

    printf "%s..." "$(_color_primary "${__notebook}")"

    local _notebook_sync_successful=0

    _syncable_count="$((_syncable_count+1))"

    if ((_sync_all))
    then # sync without prompt in background
      GIT_TERMINAL_PROMPT=0 NB_AUTO_SYNC=1 \
        _git checkpoint "${NB_NOTEBOOK_PATH}" "[nb] Sync" --spinner
    else # sync with prompt in foreground
      GIT_TERMINAL_PROMPT=1 NB_AUTO_SYNC=0 \
        _git checkpoint "${NB_NOTEBOOK_PATH}" "[nb] Sync" --spinner

      _git sync "${NB_NOTEBOOK_PATH}"
    fi && _notebook_sync_successful=1

    if ! ((_notebook_sync_successful))            ||
       _git dirty           "${NB_NOTEBOOK_PATH}" ||
       _git out_of_sync     "${NB_NOTEBOOK_PATH}"
    then
      _exit_1 cat <<HEREDOC
Sync failed.

Reason unknown, but likely one of the following:

- Authentication error or credentials not configured.
- Network unavailable.
- Misconfigured remote URL.

Configuring Git Credentials:
  https://github.com/xwmx/nb#private-repositories-and-git-credentials
HEREDOC
    fi
  done && ((_syncable_count)) && _sync_successful=1

  if ((_sync_successful))
  then
    printf "Done!\\n"
  elif ! ((_syncable_count))
  then
    _exit_1 cat <<HEREDOC
No unarchived notebooks with remotes found.

Set the remote for the current notebook:

  $(_color_primary "${_ME} remote set <url>")

Set the remote for a notebook named "example":

  $(_color_primary "${_ME} example:remote set <url>")

HEREDOC
  else
    printf "\\n"

    _exit_1 printf "Sync failed.\\n"
  fi
}

# todos ################################################################# todos

describe "todos" <<HEREDOC
Usage:
  ${_ME} todos [open | closed] [-a | --all]

Options:
  -a, --all  Search all unarchived notebooks.

Description:
  List todos.
HEREDOC
_todos() {
  local _pattern='\- \[ \]|\- \[x\]'
  local _search_all=0

  while ((${#}))
  do
    case "${1:-}" in
      -a|--all)
        _search_all=1
        ;;
      closed)
        _pattern='\- \[x\]'
        ;;
      open)
        _pattern='\- \[ \]'
        ;;
    esac

    shift
  done

  if ((_search_all))
  then
    _search "${_pattern}" --all
  else
    _search "${_pattern}"
  fi | sed -e '/^$/d'
}
_alias_subcommand "todos" "todo"

# update ############################################################### update

describe "update" <<HEREDOC
Usage:
  ${_ME} update

Description:
  Update \`${_ME}\` to the latest version. You will be prompted for
  your password if administrator privileges are required.

  If \`${_ME}\` was installed using a package manager like npm or
  Homebrew, use the package manager's upgrade functionality instead
  of this command.
HEREDOC
_update() {
  local _current_path
  _current_path="${BASH_SOURCE[0]}"

  if [[ -n "${_current_path}" ]]
  then
    if [[ -L "${_current_path}" ]]
    then
      if hash "realpath" 2>/dev/null
      then
        _current_path="$(realpath "${_current_path}")"
      else
        _current_path="$(readlink "${_current_path}")"
      fi
    fi
  fi

  if hash npm 2>/dev/null         &&
     npm list | grep -q notes.sh  &&
     [[ "${_current_path}" =~ \/node\/|\/node_modules\/ ]]
  then # installed with npm
    cat <<HEREDOC
Installed with npm. To update, run:
  npm update -g notes.sh
HEREDOC
  elif hash npm 2>/dev/null     &&
       npm list | grep -q nb.sh &&
       [[ "${_current_path}" =~ \/node\/|\/node_modules\/ ]]
  then # installed with npm
    cat <<HEREDOC
Installed with npm. To update, run:
  npm update -g nb.sh
HEREDOC
  elif [[ "${OSTYPE}" =~ ^darwin        ]] &&
       [[ "${_current_path}" =~ Cellar  ]]
  then
    cat <<HEREDOC
Installed with Homebrew. To update, run:
  brew upgrade xwmx/taps/nb
HEREDOC
  else
    local _nb_url="${_REPO_RAW_URL}/nb"

    local _temp_file
    _temp_file="$(mktemp)"

    if ! _download_from "${_nb_url}" "${_temp_file}"
    then
      _exit_1 printf "Unable to download update.\\n"
    fi

    if [[ "$(_get_hash "${_temp_file}")" != "$(_get_hash "${_current_path}")" ]]
    then
      while true
      do
        printf "Updating %s in place. " "$(_color_primary "${_ME}")"

        IFS='' read -r -e -d $'\n' -p "\
$(_color_primary "Proceed?") $(_color_brackets "y/N") " __yn

        case ${__yn} in
          [Yy]*)
            break
            ;;
          *)
            printf "Exiting...\\n"
            exit 0
            ;;
        esac
      done

      if [[ -w "${_current_path}" ]]
      then
        cat  "${_temp_file}" > "${_current_path}"
      else
        cat "${_temp_file}" | sudo tee "${_current_path}" > /dev/null
      fi

      printf "%s updated to the latest version.\\n" "$(_color_primary "${_ME}")"
      exit 0
    else
      printf "Already at the latest version.\\n"
    fi

    if [[ -n "${_temp_file}" ]]
    then
      rm "${_temp_file:?}"
    fi
  fi
}
_alias_subcommand "update" "upgrade"

# use ##################################################################### use

describe "use" <<HEREDOC
Usage:
  ${_ME} use <notebook>

Description:
  Switch to the specified notebook. Shortcut for \`${_ME} notebooks use\`.

Example:
  ${_ME} use example

Shortcut Alias: \`u\`
HEREDOC
_use() {
  local _name="${1:-}"
  if [[ -z "${_name}" ]]
  then
    _exit_1 _help use
  fi
  _notebooks use "${_name}"
}
_alias_subcommand "use" "u"

# version ############################################################# version

describe "version" <<HEREDOC
Usage:
  ${_ME} version

Description:
  Display version information.
HEREDOC
_version() {
  printf "%s\\n" "${_VERSION}"
}

###############################################################################
# Plugins
###############################################################################

# $NB_PLUGIN_SUBCOMMANDS
#
# The list of subcommands exposed by plugins.
export NB_PLUGIN_SUBCOMMANDS=()

# User defined plugins can be installed in the $NB_DIR/.plugins directory.
# Plugins have a .nb-plugin or .nb-theme extension and are written in a
# Bash-compatible shell scripting language.
#
# NOTE: Themes are loaded separately.
__load_plugins() {
  if [[ -d "${NB_DIR}/.plugins" ]]
  then
    set +f
    for __file in "${NB_DIR}/.plugins"/*.nb-plugin*
    do
      [[ -f "${__file}" ]] || continue

      source "${__file}"
    done
    set -f
  fi
}; __load_plugins

###############################################################################
# Program Option Parsing
###############################################################################

# Normalize Options ###########################################################

# Source:
#   https://github.com/e36freak/templates/blob/master/options

# Iterate over options, breaking -ab into -a -b and --foo=bar into --foo bar
# also turns -- into --endopts to avoid issues with things like '-o-', the '-'
# should not indicate the end of options, but be an invalid option (or the
# argument to the option, such as wget -qO-)
unset options
# while the number of arguments is greater than 0
while ((${#}))
do
  case "${1}" in
    # if option is of type -ab
    -[!-]?*)
      # loop over each character starting with the second
      for ((i=1; i < ${#1}; i++))
      do
        # extract 1 character from position 'i'
        c="${1:i:1}"
        # add current char to options
        options+=("-${c}")
      done
      ;;
    # if option is of type --foo=bar, split on first '='
    --?*=*)
      options+=("${1%%=*}" "${1#*=}")
      ;;
    # end of options, stop breaking them up
    --)
      options+=(--endopts)
      shift
      options+=("${@}")
      break
      ;;
    # otherwise, nothing special
    *)
      options+=("${1}")
      ;;
  esac

  shift
done
# set new positional parameters to altered options. Set default to blank.
set -- "${options[@]:-}"
unset options

# Parse Options ###############################################################

# Initialize program option variables.
_ARGUMENTS=()
_SUBCOMMAND=
_USE_DEBUG=0

# $_SUBCOMMANDS
#
# All available subcommands.
_SUBCOMMANDS=(
  a
  add
  b
  bookmark
  bookmarks
  bs
  commands
  completions
  config
  count
  create
  d
  delete
  e
  edit
  export
  env
  git
  h
  help
  helpers
  history
  import
  index
  init
  list
  ls
  move
  mv
  n
  nb
  nbs
  new
  notebook
  notebooks
  ns
  o
  open
  p
  peek
  plugin
  plugins
  preview
  q
  r
  remote
  rename
  run
  s
  search
  set
  settings
  shell
  show
  status
  subcommands
  sync
  u
  update
  upgrade
  use
  version
  view
  ${NB_PLUGIN_SUBCOMMANDS[@]:-}
)

# $_DOCUMENTED_SUBCOMMANDS
#
# Primary subcommands that appear in documentation. For example, some aliases
# are omitted from this list. This list is also used for tab completion.
_DOCUMENTED_SUBCOMMANDS=(
  a
  add
  b
  bookmark
  commands
  completions
  count
  d
  delete
  e
  edit
  export
  env
  git
  h
  help
  history
  import
  init
  list
  ls
  move
  mv
  n
  notebooks
  o
  open
  p
  peek
  plugins
  preview
  q
  remote
  rename
  run
  s
  search
  set
  settings
  shell
  show
  status
  subcommands
  sync
  u
  update
  use
  version
  ${NB_PLUGIN_SUBCOMMANDS[@]:-}
)

# $_GIT_SUBCOMMANDS
#
# Subcommands that initiate background cleanup commits and sync with remotes.
_GIT_SUBCOMMANDS=(
  a
  add
  b
  bookmark
  bookmarks
  bs
  count
  create
  d
  delete
  e
  edit
  export
  import
  list
  ls
  move
  mv
  n
  nb
  nbs
  new
  notebook
  notebooks
  ns
  o
  open
  p
  peek
  preview
  q
  rename
  s
  show
  search
  use
  u
  view
  ${NB_PLUGIN_SUBCOMMANDS[@]:-}
)

# $_SUBCOMMANDS_PATTERN
#
# The contents of the `$_SUBCOMMANDS` array, joined with '|'.
_SUBCOMMANDS_PATTERN="^$(_join '$|^' "${_SUBCOMMANDS[@]}")$"

# $_GIT_SUBCOMMANDS_PATTERN
#
# The contents of the `$_GIT_SUBCOMMANDS` array, joined with '|'.
_GIT_SUBCOMMANDS_PATTERN="^$(_join '$|^' "${_GIT_SUBCOMMANDS[@]}")$"

# _is_valid_subcommand()
#
# Usage:
#   _is_valid_subcommand <name>
#
# Exit / Error Status:
#   0 (success, true)  If the given <name> is a valid subcommand name.
#   1 (error,  false)  If not.
_is_valid_subcommand() {
  [[ -n "${1:-}" ]] &&  [[ "${1:-}" =~ ${_SUBCOMMANDS_PATTERN} ]]
}

# $__PREVIOUS_OPTION_WAS_FLAG
#
# Values: 0, 1
#
# Arithmethic boolean indicating if the previous option in the loop was a flag.
__PREVIOUS_OPTION_WAS_FLAG=0

# Parse program options.
while ((${#}))
do
  __opt="${1}"

  shift

  case "${__opt}" in
    -h|--help)
      case "${_SUBCOMMAND:-}" in
        git|run)
          _ARGUMENTS+=("${__opt}")
          ;;
        *)
          if [[ -n "${_SUBCOMMAND:-}" ]]
          then
            _ARGUMENTS+=("${_SUBCOMMAND}")
          fi

          _SUBCOMMAND="help"
          ;;
      esac
      ;;
    --debug)
      _USE_DEBUG=1
      ;;
    --no-color|--nocolor)
      _COLOR_ENABLED=0
      ;;
    --no-git|--nogit|--skip-git)
      _GIT_ENABLED=0
      ;;
    --version)
      if [[ -n "${_SUBCOMMAND:-}" ]]
      then
        _ARGUMENTS+=("${__opt}")
      else
        _SUBCOMMAND="version"
      fi
      ;;
    -i|--interactive)
      case "${_SUBCOMMAND:-}" in
        git|run)
          _ARGUMENTS+=("${__opt}")
          ;;
        *)
          _SUBCOMMAND="shell"
          ;;
      esac
      ;;
    --welcome)
      _print_welcome
      ;;
    *:*)
      if [[ -z "${_SUBCOMMAND:-}"   ]] &&
          _string_is_url "${__opt}"
      then
        _SUBCOMMAND="bookmark"
        _ARGUMENTS+=("${__opt}")
      elif _notebooks current --selected
      then
        _ARGUMENTS+=("${__opt}")
      else
        __identifier_or_subcommand_or_url="${__opt#*:}"

        if [[ -z "${__identifier_or_subcommand_or_url:-}" ]] &&
           [[ -n "${_SUBCOMMAND:-}"                       ]] &&
           [[ ! "${_SUBCOMMAND}" =~ ^ls$|^list$           ]]
        then
          _ARGUMENTS+=("${__opt}")
        else
          if ! _notebooks select "${__opt}"
          then
            if [[ -n "${_SUBCOMMAND:-}" ]]
            then
              _ARGUMENTS+=("${__opt}")
            else
              _exit_1 printf \
                "Notebook not found: %s\\n" "$(_color_primary "${__opt%%:*}")"
            fi
          fi

          if _is_valid_subcommand "${__identifier_or_subcommand_or_url}"
          then
            _SUBCOMMAND="${__identifier_or_subcommand_or_url}"
          else
            if _string_is_url "${__identifier_or_subcommand_or_url}"
            then
              _SUBCOMMAND="bookmark"
              _ARGUMENTS+=("${__identifier_or_subcommand_or_url}")
            else
              _ARGUMENTS+=("${__opt}")
            fi
          fi
        fi
      fi
      ;;
    --endopts)
      # Terminate option parsing.
      break
      ;;
    *)
      if ! ((__PREVIOUS_OPTION_WAS_FLAG)) &&
         [[ -z "${_SUBCOMMAND:-}" ]]      &&
         _is_valid_subcommand "${__opt}"
      then
        _SUBCOMMAND="${__opt}"
      else
        if [[ "${__opt}" =~ ^- ]]
        then
          __PREVIOUS_OPTION_WAS_FLAG=1
        elif ((__PREVIOUS_OPTION_WAS_FLAG))
        then
          __PREVIOUS_OPTION_WAS_FLAG=0
        fi

        _ARGUMENTS+=("${__opt}")
      fi
      ;;
  esac
done

_debug printf "\${_SUBCOMMAND}: '%s'\\n"          "${_SUBCOMMAND}"
_debug printf "\${NB_NOTEBOOK_PATH}: '%s'\\n"     "${NB_NOTEBOOK_PATH}"
_debug printf "\${_LOCAL_NOTEBOOK_PATH}: '%s'\\n" "${_LOCAL_NOTEBOOK_PATH}"
_debug printf "\${_ARGUMENTS[*:-]}: '%s'\\n"      "${_ARGUMENTS[*]:-}"

###############################################################################
# Deprecated Functions and Variables
#
# TODO: Remove
###############################################################################

# _get_notebook_identifier()
_get_notebook_identifier() { _notebooks show "${@}" --escaped; }

# _get_selection_basename()
_get_selection_basename() { _show "${@}" --filename; }

# _set_selection_notebook() <selector>
_set_selection_notebook() {
  if [[ "${1:-}" =~ : ]]
  then
    if _notebooks select "${1:-}"
    then
      _NOTEBOOK_PATH="${NB_NOTEBOOK_PATH}"
    fi
  elif [[ "${_NOTEBOOK_PATH:-}" != "${NB_NOTEBOOK_PATH}" ]]
  then
    _NOTEBOOK_PATH="${NB_NOTEBOOK_PATH}"
  fi
}

# $_NOTEBOOK_PATH
# shellcheck disable=SC2034
export _NOTEBOOK_PATH="${_NOTEBOOK_PATH:-"${NB_NOTEBOOK_PATH}"}"

# $_SCOPE
# shellcheck disable=SC2034
# _SCOPE="$(_notebooks current --name)"
_SCOPE="$(basename "${NB_NOTEBOOK_PATH}")"

# $_SCOPED
# shellcheck disable=SC2034
_SCOPED="$(
  # if _notebooks current --selected
  if [[ "${NB_NOTEBOOK_PATH}" != "${_GLOBAL_NOTEBOOK_PATH}" ]]
  then
    printf "1\\n"
  else
    printf "0\\n"
  fi
)"

# Color functions.
_highlight() { _color_primary "${@:-}"; }
_id_brackets_color() { _color_brackets "${@:-}"; }

###############################################################################
# _main()
###############################################################################

# _main()
#
# Usage:
#   _main "$@"
#
# Description:
#   Primary entry point for the program logic. Call this function at the end
#   of the script after everything has been defined.
_main() {
  if [[ -z "${_SUBCOMMAND:-}" ]]
  then
    _SUBCOMMAND="ls"
  fi

  case "${_SUBCOMMAND}" in
    init|sync)
      _git required
      "_${_SUBCOMMAND}" "${@}"
      ;;
    commands|completions|env|git|h|help*|r|run|set|settings|subcommands|update|version)
      "_${_SUBCOMMAND}" "${@}"
      ;;
    *)
      _git required

      # Call `_init()` if configuration hasn't been initialized.
      if [[ ! -e "${NB_DIR}"            ]] ||
         [[ ! -e "${NB_NOTEBOOK_PATH}"  ]]
      then
        _init
        _ls || true # returns 0 due to empty repository.
        return 0
      fi

      if [[ "${_SUBCOMMAND}" =~ ${_GIT_SUBCOMMANDS_PATTERN} ]]
      then
        if _git dirty "${NB_NOTEBOOK_PATH}"
        then
          _index reconcile

          _git checkpoint "${NB_NOTEBOOK_PATH}" --spinner
        elif _git autosyncable "${NB_NOTEBOOK_PATH}"
        then
          (_git checkpoint "${NB_NOTEBOOK_PATH}" &>/dev/null) &
        fi
      fi

      "_${_SUBCOMMAND}" "${@}"
      ;;
  esac
}

_main "${_ARGUMENTS[@]:-}"
